|
|
|
@ -7840,8 +7840,8 @@
|
|
|
|
|
* - finds the right dataType (mediates between content-type and expected dataType)
|
|
|
|
|
* - returns the corresponding response
|
|
|
|
|
*/
|
|
|
|
|
// 函数用于处理 AJAX 请求的响应
|
|
|
|
|
function ajaxHandleResponses(s, jqXHR, responses) {
|
|
|
|
|
|
|
|
|
|
var contents = s.contents,
|
|
|
|
|
dataTypes = s.dataTypes,
|
|
|
|
|
responseFields = s.responseFields,
|
|
|
|
@ -7850,14 +7850,14 @@
|
|
|
|
|
finalDataType,
|
|
|
|
|
firstDataType;
|
|
|
|
|
|
|
|
|
|
// Fill responseXXX fields
|
|
|
|
|
// 遍历 responseFields 并将响应数据存储到 jqXHR 的相应属性中
|
|
|
|
|
for (type in responseFields) {
|
|
|
|
|
if (type in responses) {
|
|
|
|
|
jqXHR[responseFields[type]] = responses[type];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove auto dataType and get content-type in the process
|
|
|
|
|
// 移除自动数据类型,并在过程中获取内容类型
|
|
|
|
|
while (dataTypes[0] === "*") {
|
|
|
|
|
dataTypes.shift();
|
|
|
|
|
if (ct === undefined) {
|
|
|
|
@ -7865,7 +7865,7 @@
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if we're dealing with a known content-type
|
|
|
|
|
// 检查是否是已知的内容类型
|
|
|
|
|
if (ct) {
|
|
|
|
|
for (type in contents) {
|
|
|
|
|
if (contents[type] && contents[type].test(ct)) {
|
|
|
|
@ -7875,11 +7875,11 @@
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check to see if we have a response for the expected dataType
|
|
|
|
|
// 检查是否有期望数据类型的响应
|
|
|
|
|
if (dataTypes[0] in responses) {
|
|
|
|
|
finalDataType = dataTypes[0];
|
|
|
|
|
} else {
|
|
|
|
|
// Try convertible dataTypes
|
|
|
|
|
// 尝试可转换的数据类型
|
|
|
|
|
for (type in responses) {
|
|
|
|
|
if (!dataTypes[0] || s.converters[type + " " + dataTypes[0]]) {
|
|
|
|
|
finalDataType = type;
|
|
|
|
@ -7889,13 +7889,12 @@
|
|
|
|
|
firstDataType = type;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Or just use first one
|
|
|
|
|
// 或者仅使用第一个数据类型
|
|
|
|
|
finalDataType = finalDataType || firstDataType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we found a dataType
|
|
|
|
|
// We add the dataType to the list if needed
|
|
|
|
|
// and return the corresponding response
|
|
|
|
|
// 如果找到了数据类型
|
|
|
|
|
// 如果需要,将数据类型添加到列表中并返回相应的响应
|
|
|
|
|
if (finalDataType) {
|
|
|
|
|
if (finalDataType!== dataTypes[0]) {
|
|
|
|
|
dataTypes.unshift(finalDataType);
|
|
|
|
@ -7904,10 +7903,9 @@
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Chain conversions given the request and the original response
|
|
|
|
|
// 根据请求和原始响应进行链式转换
|
|
|
|
|
function ajaxConvert(s, response) {
|
|
|
|
|
|
|
|
|
|
// Apply the dataFilter if provided
|
|
|
|
|
// 如果提供了数据过滤器,则应用数据过滤器
|
|
|
|
|
if (s.dataFilter) {
|
|
|
|
|
response = s.dataFilter(response, s.dataType);
|
|
|
|
|
}
|
|
|
|
@ -7918,22 +7916,20 @@
|
|
|
|
|
key,
|
|
|
|
|
length = dataTypes.length,
|
|
|
|
|
tmp,
|
|
|
|
|
// Current and previous dataTypes
|
|
|
|
|
// 当前和之前的数据类型
|
|
|
|
|
current = dataTypes[0],
|
|
|
|
|
prev,
|
|
|
|
|
// Conversion expression
|
|
|
|
|
// 转换表达式
|
|
|
|
|
conversion,
|
|
|
|
|
// Conversion function
|
|
|
|
|
// 转换函数
|
|
|
|
|
conv,
|
|
|
|
|
// Conversion functions (transitive conversion)
|
|
|
|
|
// 转换函数(传递转换)
|
|
|
|
|
conv1,
|
|
|
|
|
conv2;
|
|
|
|
|
|
|
|
|
|
// For each dataType in the chain
|
|
|
|
|
// 对于链中的每个数据类型
|
|
|
|
|
for (i = 1; i < length; i++) {
|
|
|
|
|
|
|
|
|
|
// Create converters map
|
|
|
|
|
// with lowercased keys
|
|
|
|
|
// 创建转换器映射,使用小写键
|
|
|
|
|
if (i === 1) {
|
|
|
|
|
for (key in s.converters) {
|
|
|
|
|
if (typeof key === "string") {
|
|
|
|
@ -7942,21 +7938,20 @@
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get the dataTypes
|
|
|
|
|
// 获取数据类型
|
|
|
|
|
prev = current;
|
|
|
|
|
current = dataTypes[i];
|
|
|
|
|
|
|
|
|
|
// If current is auto dataType, update it to prev
|
|
|
|
|
// 如果当前是自动数据类型,将其更新为 prev
|
|
|
|
|
if (current === "*") {
|
|
|
|
|
current = prev;
|
|
|
|
|
// If no auto and dataTypes are actually different
|
|
|
|
|
// 如果不是自动数据类型且数据类型不同
|
|
|
|
|
} else if (prev!== "*" && prev!== current) {
|
|
|
|
|
|
|
|
|
|
// Get the converter
|
|
|
|
|
// 获取转换器
|
|
|
|
|
conversion = prev + " " + current;
|
|
|
|
|
conv = converters[conversion] || converters["* " + current];
|
|
|
|
|
|
|
|
|
|
// If there is no direct converter, search transitively
|
|
|
|
|
// 如果没有直接的转换器,进行传递搜索
|
|
|
|
|
if (!conv) {
|
|
|
|
|
conv2 = undefined;
|
|
|
|
|
for (conv1 in converters) {
|
|
|
|
@ -7975,13 +7970,13 @@
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// If we found no converter, dispatch an error
|
|
|
|
|
// 如果没有找到转换器,发出错误
|
|
|
|
|
if (!(conv || conv2)) {
|
|
|
|
|
jQuery.error("No conversion from " + conversion.replace(" ", " to "));
|
|
|
|
|
}
|
|
|
|
|
// If found converter is not an equivalence
|
|
|
|
|
// 如果找到的转换器不是等价的
|
|
|
|
|
if (conv!== true) {
|
|
|
|
|
// Convert with 1 or 2 converters accordingly
|
|
|
|
|
// 相应地使用 1 或 2 个转换器进行转换
|
|
|
|
|
response = conv? conv(response) : conv2(conv1(response));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -7996,38 +7991,47 @@
|
|
|
|
|
jsre = /(\=)\?(&|$)|\?\?/i;
|
|
|
|
|
|
|
|
|
|
// Default jsonp settings
|
|
|
|
|
// 使用 jQuery.ajaxSetup 配置全局的 AJAX 设置
|
|
|
|
|
jQuery.ajaxSetup({
|
|
|
|
|
// 定义 jsonp 的参数名
|
|
|
|
|
jsonp: "callback",
|
|
|
|
|
// 定义 jsonpCallback 函数,生成一个唯一的回调函数名
|
|
|
|
|
jsonpCallback: function () {
|
|
|
|
|
return jQuery.expando + "_" + (jsc++);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Detect, normalize options and install callbacks for jsonp requests
|
|
|
|
|
// 使用 jQuery.ajaxPrefilter 对 json 和 jsonp 类型的请求进行预处理
|
|
|
|
|
jQuery.ajaxPrefilter("json jsonp", function (s, originalSettings, jqXHR) {
|
|
|
|
|
// 检查 s.data 是否为字符串且内容类型是否为 application/x-www-form-urlencoded
|
|
|
|
|
var inspectData = (typeof s.data === "string") && /^application\/x-www-form-urlencoded/.test(s.contentType);
|
|
|
|
|
|
|
|
|
|
var inspectData = ( typeof s.data === "string" ) && /^application\/x\-www\-form\-urlencoded/.test( s.contentType );
|
|
|
|
|
|
|
|
|
|
// 如果数据类型为 jsonp 或 jsonp 不为 false 且 url 或 data 中包含 jsonp 回调参数
|
|
|
|
|
if (s.dataTypes[0] === "jsonp" ||
|
|
|
|
|
s.jsonp!== false && (jsre.test(s.url) ||
|
|
|
|
|
inspectData && jsre.test(s.data))) {
|
|
|
|
|
|
|
|
|
|
var responseContainer,
|
|
|
|
|
// 获取或生成 jsonpCallback 函数
|
|
|
|
|
jsonpCallback = s.jsonpCallback =
|
|
|
|
|
jQuery.isFunction(s.jsonpCallback)? s.jsonpCallback() : s.jsonpCallback,
|
|
|
|
|
// 存储之前的 jsonpCallback 函数
|
|
|
|
|
previous = window[jsonpCallback],
|
|
|
|
|
url = s.url,
|
|
|
|
|
data = s.data,
|
|
|
|
|
replace = "$1" + jsonpCallback + "$2";
|
|
|
|
|
|
|
|
|
|
if (s.jsonp!== false) {
|
|
|
|
|
// 替换 url 中的 jsonp 回调参数
|
|
|
|
|
url = url.replace(jsre, replace);
|
|
|
|
|
if (s.url === url) {
|
|
|
|
|
if (inspectData) {
|
|
|
|
|
// 替换 data 中的 jsonp 回调参数
|
|
|
|
|
data = data.replace(jsre, replace);
|
|
|
|
|
}
|
|
|
|
|
if (s.data === data) {
|
|
|
|
|
// Add callback manually
|
|
|
|
|
// 手动添加 jsonp 回调参数到 url 中
|
|
|
|
|
url += (/\?/.test(url)? "&" : "?") + s.jsonp + "=" + jsonpCallback;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -8036,22 +8040,22 @@
|
|
|
|
|
s.url = url;
|
|
|
|
|
s.data = data;
|
|
|
|
|
|
|
|
|
|
// Install callback
|
|
|
|
|
// 安装回调函数
|
|
|
|
|
window[jsonpCallback] = function (response) {
|
|
|
|
|
responseContainer = [response];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Clean-up function
|
|
|
|
|
// 清理函数
|
|
|
|
|
jqXHR.always(function () {
|
|
|
|
|
// Set callback back to previous value
|
|
|
|
|
// 将回调函数恢复为之前的值
|
|
|
|
|
window[jsonpCallback] = previous;
|
|
|
|
|
// Call if it was a function and we have a response
|
|
|
|
|
// 如果 responseContainer 存在且 previous 是函数,则调用回调函数
|
|
|
|
|
if (responseContainer && jQuery.isFunction(previous)) {
|
|
|
|
|
window[jsonpCallback](responseContainer[0]);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Use data converter to retrieve json after script execution
|
|
|
|
|
// 使用数据转换器在脚本执行后获取 json 数据
|
|
|
|
|
s.converters["script json"] = function () {
|
|
|
|
|
if (!responseContainer) {
|
|
|
|
|
jQuery.error(jsonpCallback + " was not called");
|
|
|
|
@ -8059,26 +8063,28 @@
|
|
|
|
|
return responseContainer[0];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// force json dataType
|
|
|
|
|
// 强制使用 json 数据类型
|
|
|
|
|
s.dataTypes[0] = "json";
|
|
|
|
|
|
|
|
|
|
// Delegate to script
|
|
|
|
|
// 委托给 script 类型处理
|
|
|
|
|
return "script";
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Install script dataType
|
|
|
|
|
// 使用 jQuery.ajaxSetup 配置 script 数据类型的相关设置
|
|
|
|
|
jQuery.ajaxSetup({
|
|
|
|
|
accepts: {
|
|
|
|
|
// 定义 script 类型可接受的内容类型
|
|
|
|
|
script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
|
|
|
|
|
},
|
|
|
|
|
contents: {
|
|
|
|
|
// 定义 script 类型的内容匹配正则表达式
|
|
|
|
|
script: /javascript|ecmascript/
|
|
|
|
|
},
|
|
|
|
|
converters: {
|
|
|
|
|
// 定义 text 到 script 的数据转换器,执行全局的脚本评估
|
|
|
|
|
"text script": function (text) {
|
|
|
|
|
jQuery.globalEval(text);
|
|
|
|
|
return text;
|
|
|
|
@ -8087,66 +8093,65 @@
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Handle cache's special case and global
|
|
|
|
|
// 使用 jQuery.ajaxPrefilter 为 "script" 类型的请求添加预处理
|
|
|
|
|
jQuery.ajaxPrefilter("script", function (s) {
|
|
|
|
|
// 如果未定义 s.cache,则将其设置为 false
|
|
|
|
|
if (s.cache === undefined) {
|
|
|
|
|
s.cache = false;
|
|
|
|
|
}
|
|
|
|
|
// 如果是跨域请求
|
|
|
|
|
if (s.crossDomain) {
|
|
|
|
|
// 将请求类型设置为 GET
|
|
|
|
|
s.type = "GET";
|
|
|
|
|
// 将全局标志设置为 false
|
|
|
|
|
s.global = false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Bind script tag hack transport
|
|
|
|
|
// 绑定 script 标签的 hack 传输方式
|
|
|
|
|
jQuery.ajaxTransport("script", function (s) {
|
|
|
|
|
|
|
|
|
|
// This transport only deals with cross domain requests
|
|
|
|
|
// 这个传输方式仅处理跨域请求
|
|
|
|
|
if (s.crossDomain) {
|
|
|
|
|
|
|
|
|
|
var script,
|
|
|
|
|
// 获取文档的 head 元素
|
|
|
|
|
head = document.head || document.getElementsByTagName("head")[0] || document.documentElement;
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
|
|
send: function (_, callback) {
|
|
|
|
|
|
|
|
|
|
// 创建 script 元素
|
|
|
|
|
script = document.createElement("script");
|
|
|
|
|
|
|
|
|
|
// 设置 script 元素为异步加载
|
|
|
|
|
script.async = "async";
|
|
|
|
|
|
|
|
|
|
// 如果设置了脚本字符集,将其设置到 script 元素上
|
|
|
|
|
if (s.scriptCharset) {
|
|
|
|
|
script.charset = s.scriptCharset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 设置 script 的 src 属性为请求的 url
|
|
|
|
|
script.src = s.url;
|
|
|
|
|
|
|
|
|
|
// Attach handlers for all browsers
|
|
|
|
|
// 为 script 元素添加 onload 和 onreadystatechange 事件处理函数
|
|
|
|
|
script.onload = script.onreadystatechange = function (_, isAbort) {
|
|
|
|
|
|
|
|
|
|
// 如果是中止或者 script 的 readyState 不存在或为 loaded 或 complete 状态
|
|
|
|
|
if (isAbort ||!script.readyState || /loaded|complete/.test(script.readyState)) {
|
|
|
|
|
|
|
|
|
|
// Handle memory leak in IE
|
|
|
|
|
// 处理 IE 的内存泄漏问题
|
|
|
|
|
script.onload = script.onreadystatechange = null;
|
|
|
|
|
|
|
|
|
|
// Remove the script
|
|
|
|
|
// 移除 script 元素
|
|
|
|
|
if (head && script.parentNode) {
|
|
|
|
|
head.removeChild(script);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Dereference the script
|
|
|
|
|
// 解除对 script 的引用
|
|
|
|
|
script = undefined;
|
|
|
|
|
|
|
|
|
|
// Callback if not abort
|
|
|
|
|
// 如果不是中止,调用回调函数
|
|
|
|
|
if (!isAbort) {
|
|
|
|
|
callback(200, "success");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
// Use insertBefore instead of appendChild to circumvent an IE6 bug.
|
|
|
|
|
// This arises when a base node is used (#2709 and #4378).
|
|
|
|
|
// 使用 insertBefore 代替 appendChild 以避免 IE6 的一个 bug
|
|
|
|
|
// 当使用 base 节点时会出现此问题 (#2709 和 #4378)
|
|
|
|
|
head.insertBefore(script, head.firstChild);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// abort 函数,用于中止请求
|
|
|
|
|
abort: function () {
|
|
|
|
|
if (script) {
|
|
|
|
|
script.onload(0, 1);
|
|
|
|
@ -8159,23 +8164,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var // #5280: Internet Explorer will keep connections alive if we don't abort on unload
|
|
|
|
|
xhrOnUnloadAbort = window.ActiveXObject ? function() {
|
|
|
|
|
// Abort all pending requests
|
|
|
|
|
// #5280: Internet Explorer will keep connections alive if we don't abort on unload
|
|
|
|
|
// 定义 xhrOnUnloadAbort 函数,用于在页面卸载时中止所有挂起的请求(针对 IE)
|
|
|
|
|
var xhrOnUnloadAbort = window.ActiveXObject? function() {
|
|
|
|
|
// 中止所有挂起的请求
|
|
|
|
|
for (var key in xhrCallbacks) {
|
|
|
|
|
xhrCallbacks[key](0, 1);
|
|
|
|
|
}
|
|
|
|
|
} : false,
|
|
|
|
|
// 存储 xhr 的唯一标识符
|
|
|
|
|
xhrId = 0,
|
|
|
|
|
// 存储 xhr 的回调函数
|
|
|
|
|
xhrCallbacks;
|
|
|
|
|
|
|
|
|
|
// Functions to create xhrs
|
|
|
|
|
// 创建标准的 XHR 对象
|
|
|
|
|
function createStandardXHR() {
|
|
|
|
|
try {
|
|
|
|
|
return new window.XMLHttpRequest();
|
|
|
|
|
} catch (e) {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 创建 ActiveXHR 对象(用于 IE)
|
|
|
|
|
function createActiveXHR() {
|
|
|
|
|
try {
|
|
|
|
|
return new window.ActiveXObject("Microsoft.XMLHTTP");
|
|
|
|
@ -8184,13 +8194,11 @@
|
|
|
|
|
|
|
|
|
|
// Create the request object
|
|
|
|
|
// (This is still attached to ajaxSettings for backward compatibility)
|
|
|
|
|
// 根据浏览器环境选择创建 XHR 对象的函数,并将其赋值给 jQuery.ajaxSettings.xhr
|
|
|
|
|
jQuery.ajaxSettings.xhr = window.ActiveXObject?
|
|
|
|
|
/* Microsoft failed to properly
|
|
|
|
|
* implement the XMLHttpRequest in IE7 (can't request local files),
|
|
|
|
|
* so we use the ActiveXObject when it is available
|
|
|
|
|
* Additionally XMLHttpRequest can be disabled in IE7/IE8 so
|
|
|
|
|
* we need a fallback.
|
|
|
|
|
*/
|
|
|
|
|
// Microsoft failed to properly implement the XMLHttpRequest in IE7 (can't request local files),
|
|
|
|
|
// so we use the ActiveXObject when it is available
|
|
|
|
|
// Additionally XMLHttpRequest can be disabled in IE7/IE8 so we need a fallback.
|
|
|
|
|
function() {
|
|
|
|
|
return!this.isLocal && createStandardXHR() || createActiveXHR();
|
|
|
|
|
} :
|
|
|
|
@ -8198,6 +8206,7 @@
|
|
|
|
|
createStandardXHR;
|
|
|
|
|
|
|
|
|
|
// Determine support properties
|
|
|
|
|
// 扩展 jQuery.support 对象,判断浏览器对 ajax 和 cors 的支持情况
|
|
|
|
|
(function (xhr) {
|
|
|
|
|
jQuery.extend(jQuery.support, {
|
|
|
|
|
ajax:!!xhr,
|
|
|
|
@ -8206,84 +8215,79 @@
|
|
|
|
|
})(jQuery.ajaxSettings.xhr());
|
|
|
|
|
|
|
|
|
|
// Create transport if the browser can provide an xhr
|
|
|
|
|
// 如果浏览器支持 xhr,则创建传输对象
|
|
|
|
|
if (jQuery.support.ajax) {
|
|
|
|
|
|
|
|
|
|
jQuery.ajaxTransport(function (s) {
|
|
|
|
|
// Cross domain only allowed if supported through XMLHttpRequest
|
|
|
|
|
// 仅当通过 XMLHttpRequest 支持跨域时才允许跨域请求
|
|
|
|
|
if (!s.crossDomain || jQuery.support.cors) {
|
|
|
|
|
|
|
|
|
|
var callback;
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
send: function (headers, complete) {
|
|
|
|
|
|
|
|
|
|
// Get a new xhr
|
|
|
|
|
// 获取一个新的 xhr 对象
|
|
|
|
|
var xhr = s.xhr(),
|
|
|
|
|
handle,
|
|
|
|
|
i;
|
|
|
|
|
|
|
|
|
|
// Open the socket
|
|
|
|
|
// Passing null username, generates a login popup on Opera (#2865)
|
|
|
|
|
// 打开请求连接
|
|
|
|
|
// 传递空用户名会在 Opera 上触发登录弹窗 (#2865)
|
|
|
|
|
if (s.username) {
|
|
|
|
|
xhr.open(s.type, s.url, s.async, s.username, s.password);
|
|
|
|
|
} else {
|
|
|
|
|
xhr.open(s.type, s.url, s.async);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Apply custom fields if provided
|
|
|
|
|
// 应用自定义字段(如果提供)
|
|
|
|
|
if (s.xhrFields) {
|
|
|
|
|
for (i in s.xhrFields) {
|
|
|
|
|
xhr[i] = s.xhrFields[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Override mime type if needed
|
|
|
|
|
// 覆盖 MIME 类型(如果需要)
|
|
|
|
|
if (s.mimeType && xhr.overrideMimeType) {
|
|
|
|
|
xhr.overrideMimeType(s.mimeType);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// X-Requested-With header
|
|
|
|
|
// For cross-domain requests, seeing as conditions for a preflight are
|
|
|
|
|
// akin to a jigsaw puzzle, we simply never set it to be sure.
|
|
|
|
|
// (it can always be set on a per-request basis or even using ajaxSetup)
|
|
|
|
|
// For same-domain requests, won't change header if already provided.
|
|
|
|
|
// 对于跨域请求,由于预检条件比较复杂,我们通常不设置该头
|
|
|
|
|
// 对于同域请求,如果未提供该头则设置
|
|
|
|
|
if (!s.crossDomain &&!headers["X-Requested-With"]) {
|
|
|
|
|
headers["X-Requested-With"] = "XMLHttpRequest";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Need an extra try/catch for cross domain requests in Firefox 3
|
|
|
|
|
// 在 Firefox 3 中进行跨域请求时需要额外的 try/catch
|
|
|
|
|
try {
|
|
|
|
|
// 遍历 headers 对象,为 xhr 设置请求头
|
|
|
|
|
for (i in headers) {
|
|
|
|
|
xhr.setRequestHeader(i, headers[i]);
|
|
|
|
|
}
|
|
|
|
|
} catch (_) {}
|
|
|
|
|
|
|
|
|
|
// Do send the request
|
|
|
|
|
// This may raise an exception which is actually
|
|
|
|
|
// handled in jQuery.ajax (so no try/catch here)
|
|
|
|
|
// 发送请求
|
|
|
|
|
// 这里可能会引发异常,实际上会在 jQuery.ajax 中处理,所以这里没有 try/catch
|
|
|
|
|
xhr.send((s.hasContent && s.data) || null);
|
|
|
|
|
|
|
|
|
|
// Listener
|
|
|
|
|
// 监听器函数
|
|
|
|
|
callback = function (_, isAbort) {
|
|
|
|
|
|
|
|
|
|
var status,
|
|
|
|
|
statusText,
|
|
|
|
|
responseHeaders,
|
|
|
|
|
responses,
|
|
|
|
|
xml;
|
|
|
|
|
|
|
|
|
|
// Firefox throws exceptions when accessing properties
|
|
|
|
|
// of an xhr when a network error occured
|
|
|
|
|
// Firefox 在网络错误时访问 xhr 的属性会抛出异常
|
|
|
|
|
// http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
|
|
// Was never called and is aborted or complete
|
|
|
|
|
// 如果 callback 存在且 (是中止状态或者 xhr 的 readyState 为 4)
|
|
|
|
|
if (callback && (isAbort || xhr.readyState === 4)) {
|
|
|
|
|
|
|
|
|
|
// Only called once
|
|
|
|
|
// 只调用一次
|
|
|
|
|
callback = undefined;
|
|
|
|
|
|
|
|
|
|
// Do not keep as active anymore
|
|
|
|
|
// 不再保持活跃状态
|
|
|
|
|
if (handle) {
|
|
|
|
|
xhr.onreadystatechange = jQuery.noop;
|
|
|
|
|
if (xhrOnUnloadAbort) {
|
|
|
|
@ -8291,163 +8295,175 @@
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If it's an abort
|
|
|
|
|
// 如果是中止操作
|
|
|
|
|
if (isAbort) {
|
|
|
|
|
// Abort it manually if needed
|
|
|
|
|
// 如果需要,手动中止请求
|
|
|
|
|
if (xhr.readyState!== 4) {
|
|
|
|
|
xhr.abort();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 获取响应状态码
|
|
|
|
|
status = xhr.status;
|
|
|
|
|
// 获取所有响应头
|
|
|
|
|
responseHeaders = xhr.getAllResponseHeaders();
|
|
|
|
|
responses = {};
|
|
|
|
|
// 获取响应的 XML 数据
|
|
|
|
|
xml = xhr.responseXML;
|
|
|
|
|
|
|
|
|
|
// Construct response list
|
|
|
|
|
// 构建响应列表
|
|
|
|
|
if (xml && xml.documentElement /* #4958 */) {
|
|
|
|
|
responses.xml = xml;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// When requesting binary data, IE6-9 will throw an exception
|
|
|
|
|
// on any attempt to access responseText (#11426)
|
|
|
|
|
// 当请求二进制数据时,IE6-9 尝试访问 responseText 会抛出异常 (#11426)
|
|
|
|
|
try {
|
|
|
|
|
responses.text = xhr.responseText;
|
|
|
|
|
} catch( _ ) {
|
|
|
|
|
}
|
|
|
|
|
} catch (_) {}
|
|
|
|
|
|
|
|
|
|
// Firefox throws an exception when accessing
|
|
|
|
|
// statusText for faulty cross-domain requests
|
|
|
|
|
// Firefox 访问跨域请求的 statusText 时会抛出异常
|
|
|
|
|
try {
|
|
|
|
|
statusText = xhr.statusText;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
// We normalize with Webkit giving an empty statusText
|
|
|
|
|
// 标准化处理,Webkit 会给出空的 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 && s.isLocal &&!s.crossDomain) {
|
|
|
|
|
status = responses.text? 200 : 404;
|
|
|
|
|
// IE - #1450: sometimes returns 1223 when it should be 204
|
|
|
|
|
// IE - #1450: 有时应返回 204 却返回 1223
|
|
|
|
|
} else if (status === 1223) {
|
|
|
|
|
status = 204;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (firefoxAccessException) {
|
|
|
|
|
// 如果不是中止操作
|
|
|
|
|
if (!isAbort) {
|
|
|
|
|
complete(-1, firefoxAccessException);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Call complete if needed
|
|
|
|
|
// 如果需要,调用 complete 函数
|
|
|
|
|
if (responses) {
|
|
|
|
|
complete(status, statusText, responses, responseHeaders);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// if we're in sync mode or it's in cache
|
|
|
|
|
// and has been retrieved directly (IE6 & IE7)
|
|
|
|
|
// we need to manually fire the callback
|
|
|
|
|
// 如果是同步模式或者在缓存中且直接获取到(IE6 和 IE7)
|
|
|
|
|
// 我们需要手动触发回调
|
|
|
|
|
if (!s.async || xhr.readyState === 4) {
|
|
|
|
|
callback();
|
|
|
|
|
} else {
|
|
|
|
|
handle = ++xhrId;
|
|
|
|
|
if (xhrOnUnloadAbort) {
|
|
|
|
|
// Create the active xhrs callbacks list if needed
|
|
|
|
|
// and attach the unload handler
|
|
|
|
|
// 如果需要,创建活跃的 xhrs 回调列表并添加 unload 处理器
|
|
|
|
|
if (!xhrCallbacks) {
|
|
|
|
|
xhrCallbacks = {};
|
|
|
|
|
jQuery(window).unload(xhrOnUnloadAbort);
|
|
|
|
|
}
|
|
|
|
|
// Add to list of active xhrs callbacks
|
|
|
|
|
// 添加到活跃的 xhrs 回调列表
|
|
|
|
|
xhrCallbacks[handle] = callback;
|
|
|
|
|
}
|
|
|
|
|
// 设置 xhr 的 onreadystatechange 事件处理函数为 callback
|
|
|
|
|
xhr.onreadystatechange = callback;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// abort 函数,用于中止请求
|
|
|
|
|
abort: function () {
|
|
|
|
|
if (callback) {
|
|
|
|
|
callback(0, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 存储元素的默认显示属性
|
|
|
|
|
var elemdisplay = {},
|
|
|
|
|
// 存储 iframe 元素
|
|
|
|
|
iframe, iframeDoc,
|
|
|
|
|
// 用于匹配 toggle、show 或 hide 的正则表达式
|
|
|
|
|
rfxtypes = /^(?:toggle|show|hide)$/,
|
|
|
|
|
// 用于匹配数字和单位的正则表达式
|
|
|
|
|
rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
|
|
|
|
|
// 存储定时器 ID
|
|
|
|
|
timerId,
|
|
|
|
|
// 存储不同类型动画涉及的属性数组
|
|
|
|
|
fxAttrs = [
|
|
|
|
|
// height animations
|
|
|
|
|
// 高度相关动画涉及的属性
|
|
|
|
|
["height", "marginTop", "marginBottom", "paddingTop", "paddingBottom"],
|
|
|
|
|
// width animations
|
|
|
|
|
// 宽度相关动画涉及的属性
|
|
|
|
|
["width", "marginLeft", "marginRight", "paddingLeft", "paddingRight"],
|
|
|
|
|
// opacity animations
|
|
|
|
|
// 透明度相关动画涉及的属性
|
|
|
|
|
["opacity"]
|
|
|
|
|
],
|
|
|
|
|
// 存储当前动画的时间
|
|
|
|
|
fxNow;
|
|
|
|
|
|
|
|
|
|
// 扩展 jQuery.fn 对象
|
|
|
|
|
jQuery.fn.extend({
|
|
|
|
|
show: function (speed, easing, callback) {
|
|
|
|
|
var elem, display;
|
|
|
|
|
|
|
|
|
|
// 如果指定了速度(包括速度为 0)
|
|
|
|
|
if (speed || speed === 0) {
|
|
|
|
|
// 使用 animate 函数,根据 genFx 生成的显示属性进行动画显示操作,并指定速度、缓动和回调
|
|
|
|
|
return this.animate(genFx("show", 3), speed, easing, callback);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// 遍历元素集合
|
|
|
|
|
for (var i = 0, j = this.length; i < j; i++) {
|
|
|
|
|
elem = this[i];
|
|
|
|
|
|
|
|
|
|
if (elem.style) {
|
|
|
|
|
// 获取元素的显示样式
|
|
|
|
|
display = elem.style.display;
|
|
|
|
|
|
|
|
|
|
// Reset the inline display of this element to learn if it is
|
|
|
|
|
// being hidden by cascaded rules or not
|
|
|
|
|
// 如果元素没有存储 "olddisplay" 数据且显示样式为 "none"
|
|
|
|
|
if (!jQuery._data(elem, "olddisplay") && display === "none") {
|
|
|
|
|
// 重置元素的显示样式为空字符串
|
|
|
|
|
display = elem.style.display = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set elements which have been overridden with display: none
|
|
|
|
|
// in a stylesheet to whatever the default browser style is
|
|
|
|
|
// for such an element
|
|
|
|
|
// 如果元素的显示样式为空且 jQuery.css 显示为 "none" 或元素不在文档元素中
|
|
|
|
|
if ((display === "" && jQuery.css(elem, "display") === "none") ||
|
|
|
|
|
!jQuery.contains(elem.ownerDocument.documentElement, elem)) {
|
|
|
|
|
// 存储元素的默认显示样式
|
|
|
|
|
jQuery._data(elem, "olddisplay", defaultDisplay(elem.nodeName));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the display of most of the elements in a second loop
|
|
|
|
|
// to avoid the constant reflow
|
|
|
|
|
// 第二个循环设置元素的显示样式,避免频繁重排
|
|
|
|
|
for (i = 0; i < j; i++) {
|
|
|
|
|
elem = this[i];
|
|
|
|
|
|
|
|
|
|
if (elem.style) {
|
|
|
|
|
display = elem.style.display;
|
|
|
|
|
|
|
|
|
|
// 如果元素的显示样式为空或为 "none"
|
|
|
|
|
if (display === "" || display === "none") {
|
|
|
|
|
// 将元素的显示样式设置为存储的 "olddisplay" 数据或空字符串
|
|
|
|
|
elem.style.display = jQuery._data(elem, "olddisplay") || "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 返回 jQuery 对象本身
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
hide: function( speed, easing, callback ) {
|
|
|
|
|
// 如果指定了速度(包括速度为 0)
|
|
|
|
|
if ( speed || speed === 0 ) {
|
|
|
|
|
// 使用 animate 函数,根据 genFx 生成的隐藏属性进行动画隐藏操作,并指定速度、缓动和回调
|
|
|
|
|
return this.animate( genFx("hide", 3), speed, easing, callback);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
@ -8455,70 +8471,84 @@
|
|
|
|
|
i = 0,
|
|
|
|
|
j = this.length;
|
|
|
|
|
|
|
|
|
|
// 遍历元素集合
|
|
|
|
|
for ( ; i < j; i++ ) {
|
|
|
|
|
elem = this[i];
|
|
|
|
|
if ( elem.style ) {
|
|
|
|
|
// 获取元素的显示样式
|
|
|
|
|
display = jQuery.css( elem, "display" );
|
|
|
|
|
|
|
|
|
|
// 如果元素当前显示样式不为 "none" 且元素没有存储 "olddisplay" 数据
|
|
|
|
|
if ( display!== "none" &&!jQuery._data( elem, "olddisplay" ) ) {
|
|
|
|
|
// 存储元素的当前显示样式
|
|
|
|
|
jQuery._data( elem, "olddisplay", display );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the display of the elements in a second loop
|
|
|
|
|
// to avoid the constant reflow
|
|
|
|
|
// 第二个循环设置元素的显示样式为 "none"
|
|
|
|
|
// 避免频繁的重排
|
|
|
|
|
for ( i = 0; i < j; i++ ) {
|
|
|
|
|
if ( this[i].style ) {
|
|
|
|
|
this[i].style.display = "none";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 返回 jQuery 对象本身
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Save the old toggle function
|
|
|
|
|
// 保存旧的 toggle 函数
|
|
|
|
|
_toggle: jQuery.fn.toggle,
|
|
|
|
|
|
|
|
|
|
toggle: function( fn, fn2, callback ) {
|
|
|
|
|
var bool = typeof fn === "boolean";
|
|
|
|
|
|
|
|
|
|
// 如果 fn 和 fn2 都是函数
|
|
|
|
|
if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
|
|
|
|
|
// 应用旧的 toggle 函数
|
|
|
|
|
this._toggle.apply( this, arguments );
|
|
|
|
|
|
|
|
|
|
} else if ( fn == null || bool ) {
|
|
|
|
|
// 遍历元素集合
|
|
|
|
|
this.each(function() {
|
|
|
|
|
var state = bool? fn : jQuery(this).is(":hidden");
|
|
|
|
|
// 根据元素状态进行显示或隐藏操作
|
|
|
|
|
jQuery(this)[ state? "show" : "hide" ]();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// 使用 animate 函数,根据 genFx 生成的切换属性进行动画切换操作,并指定速度、缓动和回调
|
|
|
|
|
this.animate(genFx("toggle", 3), fn, fn2, callback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 返回 jQuery 对象本身
|
|
|
|
|
return this;
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fadeTo: function( speed, to, easing, callback ) {
|
|
|
|
|
// 过滤出隐藏的元素,将其不透明度设置为 0,显示它们,回到之前的状态,并执行动画
|
|
|
|
|
return this.filter(":hidden").css("opacity", 0).show().end()
|
|
|
|
|
.animate({opacity: to}, speed, easing, callback);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
animate: function( prop, speed, easing, callback ) {
|
|
|
|
|
// 使用 jQuery.speed 函数生成动画的速度、缓动和回调等配置信息
|
|
|
|
|
var optall = jQuery.speed( speed, easing, callback );
|
|
|
|
|
|
|
|
|
|
// 如果传入的 prop 是空对象,对每个元素调用 optall.complete 函数并传入 false 作为参数
|
|
|
|
|
if ( jQuery.isEmptyObject( prop ) ) {
|
|
|
|
|
return this.each( optall.complete, [ false ] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Do not change referenced properties as per-property easing will be lost
|
|
|
|
|
// 扩展一个新的对象存储 prop,避免修改原有的引用,防止丢失每个属性的缓动设置
|
|
|
|
|
prop = jQuery.extend( {}, prop );
|
|
|
|
|
|
|
|
|
|
function doAnimation() {
|
|
|
|
|
// XXX 'this' does not always have a nodeName when running the
|
|
|
|
|
// test suite
|
|
|
|
|
// 在测试套件中,'this' 可能并不总是有 nodeName 属性
|
|
|
|
|
|
|
|
|
|
// 如果 optall.queue 为 false,标记元素
|
|
|
|
|
if ( optall.queue === false ) {
|
|
|
|
|
jQuery._mark( this );
|
|
|
|
|
}
|
|
|
|
@ -8530,23 +8560,25 @@
|
|
|
|
|
parts, start, end, unit,
|
|
|
|
|
method;
|
|
|
|
|
|
|
|
|
|
// will store per property easing and be used to determine when an animation is complete
|
|
|
|
|
// 存储每个属性的缓动信息,并用于判断动画是否完成
|
|
|
|
|
opt.animatedProperties = {};
|
|
|
|
|
|
|
|
|
|
// first pass over propertys to expand / normalize
|
|
|
|
|
// 第一次遍历属性,进行扩展和规范化处理
|
|
|
|
|
for ( p in prop ) {
|
|
|
|
|
name = jQuery.camelCase( p );
|
|
|
|
|
if ( p!== name ) {
|
|
|
|
|
// 将转换后的属性名作为键,存储原属性值,并删除原属性键
|
|
|
|
|
prop[ name ] = prop[ p ];
|
|
|
|
|
delete prop[ p ];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( ( hooks = jQuery.cssHooks[ name ] ) && "expand" in hooks ) {
|
|
|
|
|
// 使用 cssHooks 的 expand 方法扩展属性值
|
|
|
|
|
replace = hooks.expand( prop[ name ] );
|
|
|
|
|
delete prop[ name ];
|
|
|
|
|
|
|
|
|
|
// not quite $.extend, this wont overwrite keys already present.
|
|
|
|
|
// also - reusing 'p' from above because we have the correct "name"
|
|
|
|
|
// 并非完全的 jQuery.extend,不会覆盖已有的键
|
|
|
|
|
// 这里复用上面的 'p',因为我们有正确的 "name"
|
|
|
|
|
for ( p in replace ) {
|
|
|
|
|
if (! ( p in prop ) ) {
|
|
|
|
|
prop[ p ] = replace[ p ];
|
|
|
|
@ -8554,6 +8586,7 @@
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for ( name in prop ) {
|
|
|
|
|
val = prop[ name ];
|
|
|
|
|