diff --git a/web/static/script/jquery-1.7.2.js b/web/static/script/jquery-1.7.2.js index dbb890e..b124fe1 100644 --- a/web/static/script/jquery-1.7.2.js +++ b/web/static/script/jquery-1.7.2.js @@ -6844,2850 +6844,2545 @@ return val + "px"; } -// 使用 jQuery 的 each 方法遍历数组 ["height", "width"] jQuery.each([ "height", "width" ], function( i, name ) { - // 为 jQuery 的 cssHooks 对象添加属性,属性名为当前遍历到的元素(height 或 width) jQuery.cssHooks[ name ] = { - // 定义获取元素样式属性的函数 get: function( elem, computed, extra ) { - // 如果 computed 参数为 true if ( computed ) { - // 如果元素的偏移宽度不为 0 - if ( elem.offsetWidth!== 0 ) { - // 调用 getWidthOrHeight 函数获取元素的宽度或高度 + if ( elem.offsetWidth !== 0 ) { return getWidthOrHeight( elem, name, extra ); } else { - // 否则使用 jQuery 的 swap 方法交换元素样式,然后调用 getWidthOrHeight 函数 return jQuery.swap( elem, cssShow, function() { return getWidthOrHeight( elem, name, extra ); }); } } }, - // 定义设置元素样式属性的函数 + set: function( elem, value ) { - // 如果 value 是数字,则添加 px 后缀,否则直接返回 value - return rnum.test( value )? + return rnum.test( value ) ? value + "px" : value; } }; }); -// 如果 jQuery 不支持 opacity 属性 - if (!jQuery.support.opacity ) { - // 为 jQuery 的 cssHooks 对象添加 opacity 属性 + if ( !jQuery.support.opacity ) { jQuery.cssHooks.opacity = { - // 定义获取元素不透明度的函数 get: function( elem, computed ) { - // IE 使用 filters 来实现不透明度 - return ropacity.test( (computed && elem.currentStyle? elem.currentStyle.filter : elem.style.filter) || "" )? - // 如果有不透明度,提取其值并除以 100 转换为 0-1 的范围 + // IE uses filters for opacity + return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ? ( parseFloat( RegExp.$1 ) / 100 ) + "" : - // 如果 computed 为 true,返回 1,否则返回空字符串 - computed? "1" : ""; + computed ? "1" : ""; }, - // 定义设置元素不透明度的函数 + set: function( elem, value ) { var style = elem.style, currentStyle = elem.currentStyle, - // 根据 value 生成不透明度的字符串表示 - opacity = jQuery.isNumeric( value )? "alpha(opacity=" + value * 100 + ")" : "", - // 获取元素的 filter 样式 + opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "", filter = currentStyle && currentStyle.filter || style.filter || ""; - // IE 在没有布局的情况下处理不透明度会有问题,通过设置 zoom 为 1 来强制布局 + + // IE has trouble with opacity if it does not have layout + // Force it by setting the zoom level style.zoom = 1; - // 如果设置不透明度为 1,且没有其他 filters 存在,尝试移除 filter 属性 + + // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652 if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" ) { - // 设置 style.filter 为 null、"" 或 " " 仍然会在 cssText 中保留 "filter:",会禁用 clearType,使用 removeAttribute 方法移除 - // style.removeAttribute 仅在 IE 中有效,但此代码路径似乎也仅在 IE 中使用 + + // Setting style.filter to null, "" & " " still leave "filter:" in the cssText + // if "filter:" is present at all, clearType is disabled, we want to avoid this + // style.removeAttribute is IE Only, but so apparently is this code path... style.removeAttribute( "filter" ); - // 如果没有通过 CSS 规则应用 filter 样式,完成操作 - if ( currentStyle &&!currentStyle.filter ) { + + // if there there is no filter style applied in a css rule, we are done + if ( currentStyle && !currentStyle.filter ) { return; } } - // 否则,更新 filter 的值 - style.filter = ralpha.test( filter )? - // 如果 filter 中已经存在不透明度相关设置,替换其值 + + // otherwise, set new filter values + style.filter = ralpha.test( filter ) ? filter.replace( ralpha, opacity ) : - // 否则添加不透明度相关设置 filter + " " + opacity; } }; } - // otherwise, set new filter values - // 根据条件设置元素的 filter 样式 - style.filter = ralpha.test( filter )? - // 如果 filter 满足 ralpha 的测试,将 filter 中匹配 ralpha 的部分替换为 opacity - filter.replace( ralpha, opacity ) : - // 否则将 filter 与 opacity 拼接 - filter + " " + opacity; -}; -}; - -// 当 DOM 加载完成后执行以下函数 -jQuery(function() { - // 这个钩子在 DOM 加载完成后才能添加,因为对其的支持测试在 DOM 加载完成后才运行 - if (!jQuery.support.reliableMarginRight ) { - // 为 jQuery 的 cssHooks 对象添加 marginRight 属性 - jQuery.cssHooks.marginRight = { - // 定义获取元素 marginRight 属性的函数 - get: function( elem, computed ) { - // WebKit Bug 13343:getComputedStyle 会返回错误的 margin-right 值,通过将元素临时设置为 inline-block 来解决 - return jQuery.swap( elem, { "display": "inline-block" }, function() { - // 如果 computed 为 true,使用 curCSS 获取元素的 margin-right 属性 - if ( computed ) { - return curCSS( elem, "margin-right" ); - } else { - // 否则直接返回元素的 marginRight 样式属性 - return elem.style.marginRight; - } - }); - } - }; - } -}); - -// 如果 jQuery.expr 和 jQuery.expr.filters 存在 -if ( jQuery.expr && jQuery.expr.filters ) { - // 为 jQuery.expr.filters 添加 hidden 过滤器 - jQuery.expr.filters.hidden = function( elem ) { - var width = elem.offsetWidth, - height = elem.offsetHeight; - // 判断元素是否隐藏,根据元素的宽高是否都为 0 或者显示样式为 none 来判断 - return ( width === 0 && height === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none"); - }; - // 为 jQuery.expr.filters 添加 visible 过滤器,它是 hidden 的反义 - jQuery.expr.filters.visible = function( elem ) { - return!jQuery.expr.filters.hidden( elem ); - }; -} - -// 这些钩子被用于 animate 来扩展属性 -jQuery.each({ - margin: "", - padding: "", - border: "Width" -}, function( prefix, suffix ) { - // 为 jQuery 的 cssHooks 对象添加属性,属性名为 prefix 与 suffix 组合 - jQuery.cssHooks[ prefix + suffix ] = { - // 定义 expand 函数 - expand: function( value ) { - var i, - // 如果 value 不是字符串,假设它是一个单独的数字,否则将其分割为数组 - parts = typeof value === "string"? value.split(" ") : [ value ], - expanded = {}; - // 遍历 4 次,将值分配给不同的边 - for ( i = 0; i < 4; i++ ) { - expanded[ prefix + cssExpand[ i ] + suffix ] = - parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; - } - // 返回扩展后的对象 - return expanded; - } - }; -}); - - - -// 定义一个全局变量 r20,用于匹配全局的 %20 字符串 -var r20 = /%20/g, - // 定义一个全局变量 rbracket,用于匹配以 [] 结尾的字符串 - rbracket = /\[\]$/, - // 定义一个全局变量 rCRLF,用于匹配回车符和换行符(可能包含或不包含回车符) - rCRLF = /\r?\n/g, - // 定义一个全局变量 rhash,用于匹配以 # 开始并包含其后所有内容的字符串 - rhash = /#.*$/, - // 定义一个全局变量 rheaders,用于匹配 HTTP 头信息格式,包括头名称和头值,IE 会在行尾留下一个 \r 字符 - rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, - // 定义一个全局变量 rinput,用于匹配特定的输入类型(如文本、日期、密码等),不区分大小写 - rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i, - // 用于检测本地协议的正则表达式,包括 about、app 等 - rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/, - // 定义一个全局变量 rnoContent,用于匹配 GET 或 HEAD 请求 - rnoContent = /^(?:GET|HEAD)$/, - // 定义一个全局变量 rprotocol,用于匹配以 // 开始的字符串 - rprotocol = /^\/\//, - // 定义一个全局变量 rquery,用于匹配问号字符 - rquery = /\?/, - // 定义一个全局变量 rscript,用于匹配 <script> 标签及其内容,不区分大小写 - rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, - // 定义一个全局变量 rselectTextarea,用于匹配 select 或 textarea 元素,不区分大小写 - rselectTextarea = /^(?:select|textarea)/i, - // 定义一个全局变量 rspacesAjax,用于匹配一个或多个空格字符 - rspacesAjax = /\s+/, - // 定义一个全局变量 rts,用于匹配包含 _= 的查询参数 - rts = /([?&])_=[^&]*/, - // 定义一个全局变量 rurl,用于解析 URL,包括协议、域名、端口等部分 - rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/, - - // 保存 jQuery.fn.load 方法的原始引用 - _load = jQuery.fn.load, - - /* Prefilters - * 1) 它们可用于引入自定义数据类型(可参考 ajax/jsonp.js 中的示例) - * 2) 它们的调用时机: - * - 在请求传输之前 - * - 在参数序列化之后(如果 s.processData 为 true,s.data 是一个字符串) - * 3) key 是数据类型 - * 4) 可以使用通配符 "*" - * 5) 执行将从传输的数据类型开始,如果需要,继续向下执行到 "*" - */ - prefilters = {}, - - /* Transports bindings - * 1) key 是数据类型 - * 2) 可以使用通配符 "*" - * 3) 选择将从传输的数据类型开始,如果需要,继续到 "*" - */ - transports = {}, - - // 存储文档的位置 - ajaxLocation, - // 存储文档位置的各个部分 - ajaxLocParts, - // 避免注释序言字符序列(#10098);必须满足代码检查工具的要求并避免压缩问题 - allTypes = ["*/"] + ["*"]; - -// #8138,IE 可能在访问 window.location 的字段时抛出异常,如果 document.domain 已被设置 -try { - // 尝试获取当前页面的 URL - ajaxLocation = location.href; -} catch( e ) { - // 使用一个 A 元素的 href 属性,因为 IE 会根据 document.location 修改它 - ajaxLocation = document.createElement( "a" ); - ajaxLocation.href = ""; - ajaxLocation = ajaxLocation.href; -} - -// 使用 rurl 正则表达式将 ajaxLocation 解析为各个部分,如果无法解析则返回空数组 -ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; - -// 为 jQuery.ajaxPrefilter 和 jQuery.ajaxTransport 提供的基础“构造函数” -function addToPrefiltersOrTransports( structure ) { - // dataTypeExpression 是可选的,默认值为 "*" - return function( dataTypeExpression, func ) { - // 如果 dataTypeExpression 不是字符串 - if ( typeof dataTypeExpression!== "string" ) { - func = dataTypeExpression; - dataTypeExpression = "*"; - } - - // 如果 func 是一个函数 - if ( jQuery.isFunction( func ) ) { - // 将 dataTypeExpression 按空格分割为多个数据类型并存储在 dataTypes 数组中 - var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ), - i = 0, - // 获取 dataTypes 数组的长度 - length = dataTypes.length, - dataType, - list, - placeBefore; - - // 遍历 dataTypes 数组中的每个数据类型 - for ( ; i < length; i++ ) { - dataType = dataTypes[ i ]; - // 检查是否需要将 func 放在现有元素之前 - placeBefore = /^\+/.test( dataType ); - if ( placeBefore ) { - // 去掉开头的 + 号,如果没有数据类型则使用 "*" - dataType = dataType.substr( 1 ) || "*"; - } - // 获取或创建结构中该数据类型的列表 - list = structure[ dataType ] = structure[ dataType ] || []; - // 根据 placeBefore 的结果,将 func 插入到列表的头部或尾部 - list[ placeBefore? "unshift" : "push" ]( func ); - } - } - }; -} -// Base inspection function for prefilters and transports -// 用于检查预过滤器或传输器的函数 -function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR, - dataType /* internal */, inspected /* internal */ ) { - // 如果未提供 dataType,则使用 options.dataTypes 的第一个元素作为 dataType - dataType = dataType || options.dataTypes[ 0 ]; - // 如果未提供 inspected,则创建一个新的 inspected 对象 - inspected = inspected || {}; - // 将当前 dataType 标记为已检查 - inspected[ dataType ] = true; - // 获取 structure 中当前 dataType 对应的列表 - var list = structure[ dataType ], - i = 0, - // 获取列表的长度,如果列表不存在则为 0 - length = list? list.length : 0, - // 表示是否仅执行操作,根据 structure 是否为 prefilters 来判断 - executeOnly = ( structure === prefilters ), - selection; - // 遍历列表中的元素 - for ( ; i < length && ( executeOnly ||!selection ); i++ ) { - // 调用列表中的函数并存储结果 - selection = list[ i ]( options, originalOptions, jqXHR ); - // 如果结果是一个字符串(表示重定向到另一个 dataType) - if ( typeof selection === "string" ) { - // 如果仅执行操作且已经检查过该 dataType,将 selection 设为 undefined - if (!executeOnly || inspected[ selection ] ) { - selection = undefined; - } else { - // 否则将新的 dataType 加入 options.dataTypes 的头部 - options.dataTypes.unshift( selection ); - // 递归调用 inspectPrefiltersOrTransports 函数 - selection = inspectPrefiltersOrTransports( - structure, options, originalOptions, jqXHR, selection, inspected ); - } - } - } - // 如果仅执行操作或没有选择,并且未检查过通配符 "*" - if ( ( executeOnly ||!selection ) &&!inspected[ "*" ] ) { - // 对通配符 "*" 调用 inspectPrefiltersOrTransports 函数 - selection = inspectPrefiltersOrTransports( - structure, options, originalOptions, jqXHR, "*", inspected ); - } - // 当仅执行操作时此返回值不必要(对于预过滤器),但调用者会忽略它 - return selection; -} - -// 一个特殊的 jQuery 扩展函数,用于 AJAX 选项,它只接受 "扁平" 选项(不进行深度扩展),解决 #9887 问题 -function ajaxExtend( target, src ) { - var key, deep, - // 获取 jQuery.ajaxSettings 中的 flatOptions 或一个空对象 - flatOptions = jQuery.ajaxSettings.flatOptions || {}; - // 遍历 src 对象的键 - for ( key in src ) { - // 如果 src[key] 不为 undefined - if ( src[ key ]!== undefined ) { - // 根据 flatOptions 决定是否使用深度扩展 - ( flatOptions[ key ]? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; - } - } - // 如果需要深度扩展,使用 jQuery.extend 进行深度扩展 - if ( deep ) { - jQuery.extend( true, target, deep ); - } -} - -// 扩展 jQuery.fn 对象 -jQuery.fn.extend({ - // load 方法的实现 - load: function( url, params, callback ) { - // 如果 url 不是字符串且 _load 存在,调用 _load 方法 - if ( typeof url!== "string" && _load ) { - return _load.apply( this, arguments ); - } - // 如果没有元素请求,直接返回 jQuery 对象 - else if (!this.length ) { - return this; - } - // 查找 url 中第一个空格的位置 - var off = url.indexOf( " " ); - if ( off >= 0 ) { - // 提取选择器和 url - var selector = url.slice( off, url.length ); - url = url.slice( 0, off ); - } - // 默认使用 GET 请求 - var type = "GET"; - // 如果提供了第二个参数 - if ( params ) { - // 如果是函数,将其作为回调函数 - if ( jQuery.isFunction( params ) ) { - callback = params; - params = undefined; - } - // 如果是对象,将其转换为参数字符串并使用 POST 请求 - else if ( typeof params === "object" ) { - params = jQuery.param( params, jQuery.ajaxSettings.traditional ); - type = "POST"; - } - } - var self = this; - // 发起 AJAX 请求 - jQuery.ajax({ - url: url, - type: type, - dataType: "html", - data: params, - // 完成回调函数 - complete: function( jqXHR, status, responseText ) { - // 存储 jqXHR 对象的响应文本 - responseText = jqXHR.responseText; - // 如果请求成功 - if ( jqXHR.isResolved() ) { - // 如果存在数据过滤器,获取实际的响应 - jqXHR.done(function( r ) { - responseText = r; - }); - // 如果指定了选择器 - self.html( selector? - // 创建一个临时 div 元素,将响应文本添加进去并移除脚本元素 - jQuery("<div>") - .append(responseText.replace(rscript, "")) - // 查找选择器匹配的元素 - .find(selector) : - // 否则直接将响应文本添加进去 - responseText ); - } - // 如果有回调函数,对每个元素调用回调函数 - if ( callback ) { - self.each( callback, [ responseText, status, jqXHR ] ); - } - } - }); - // 返回 jQuery 对象 - return this; - }, - // 序列化方法,将表单元素序列化为参数字符串 - serialize: function() { - return jQuery.param( this.serializeArray() ); - }, - // 序列化数组方法,将表单元素转换为数组 - serializeArray: function() { - return this.map(function(){ - // 如果元素有 elements 属性,将其转换为数组,否则直接使用元素本身 - return this.elements? jQuery.makeArray( this.elements ) : this; - }) - .filter(function(){ - // 过滤出有名称、未禁用且满足一定条件的元素 - return this.name &&!this.disabled && - ( this.checked || rselectTextarea.test( this.nodeName ) || - rinput.test( this.type ) ); - }) - .map(function( i, elem ){ - var val = jQuery( this ).val(); - // 处理元素的值,替换 CRLF 并构建对象 - return val == null? - null : - jQuery.isArray( val )? - jQuery.map( val, function( val, i ){ - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - }) : - { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - }).get(); - } -}); - -jQuery.extend({ - // 定义 getScript 方法,用于加载并执行 JavaScript 脚本 - getScript: function( url, callback ) { - // 使用 jQuery.get 方法请求 url,将回调函数作为第三个参数,数据类型为 "script" - return jQuery.get( url, undefined, callback, "script" ); - }, - // 定义 getJSON 方法,用于获取 JSON 数据 - getJSON: function( url, data, callback ) { - // 使用 jQuery.get 方法请求 url,将数据作为第二个参数,回调函数作为第三个参数,数据类型为 "json" - return jQuery.get( url, data, callback, "json" ); - }, - // 创建一个完整的设置对象,包含 ajaxSettings 和 settings 字段,如果 target 未提供,则写入 ajaxSettings - ajaxSetup: function( target, settings ) { - if ( settings ) { - // 构建一个设置对象,将 jQuery.ajaxSettings 扩展到 target 中 - ajaxExtend( target, jQuery.ajaxSettings ); - } else { - // 如果没有提供 settings,将 target 作为 settings,并将 jQuery.ajaxSettings 作为 target - settings = target; - target = jQuery.ajaxSettings; - } - // 将 settings 扩展到 target 中 - ajaxExtend( target, settings ); - return target; - }, - ajaxSettings: { - // 默认的请求 URL,使用之前获取的 ajaxLocation - url: ajaxLocation, - // 判断是否为本地请求,使用 rlocalProtocol 正则表达式对 ajaxLocParts[1] 进行测试 - isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ), - // 是否触发全局事件 - global: true, - // 默认请求类型为 GET - type: "GET", - // 默认的内容类型 - contentType: "application/x-www-form-urlencoded; charset=UTF-8", - // 是否处理数据 - processData: true, - // 是否异步请求 - async: true, - /* - timeout: 0, - data: null, - dataType: null, - username: null, - password: null, - cache: null, - traditional: false, - headers: {}, - */ - // 可接受的数据类型及其对应的 MIME 类型 - accepts: { - xml: "application/xml, text/xml", - html: "text/html", - text: "text/plain", - json: "application/json, text/javascript", - "*": allTypes - }, - // 内容类型的匹配正则表达式 - contents: { - xml: /xml/, - html: /html/, - json: /json/ - }, - // 响应字段的映射 - responseFields: { - xml: "responseXML", - text: "responseText" - }, - // 数据转换器列表 - // 1) 键的格式为 "source_type destination_type"(中间有一个空格) - // 2) 可以使用通配符 "*" 作为 source_type - converters: { - // 将任何类型转换为文本 - "* text": window.String, - // 文本到 HTML(true 表示不转换) - "text html": true, - // 将文本解析为 JSON 表达式 - "text json": jQuery.parseJSON, - // 将文本解析为 XML - "text xml": jQuery.parseXML - }, - // 对于不应该深度扩展的选项 - // 可以在这里添加自己的自定义选项,如果创建了一个不应该深度扩展的选项(见 ajaxExtend) - flatOptions: { - context: true, - url: true - } - }, - // 使用 addToPrefiltersOrTransports 函数为 prefilters 添加 ajaxPrefilter - ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), - // 使用 addToPrefiltersOrTransports 函数为 transports 添加 ajaxTransport - ajaxTransport: addToPrefiltersOrTransports( transports ), - // 主方法,用于发送 AJAX 请求 - ajax: function( url, options ) { - // 如果 url 是一个对象,模拟 1.5 版本之前的签名 - if ( typeof url === "object" ) { - options = url; - url = undefined; - } - // 强制 options 为对象 - options = options || {}; - var - // 创建最终的选项对象,使用 ajaxSetup 函数 - s = jQuery.ajaxSetup( {}, options ), - // 回调函数的上下文 - callbackContext = s.context || s, - // 全局事件的上下文 - // 如果 options 中提供了回调上下文且为 DOM 节点或 jQuery 集合,则使用 jQuery 包装它,否则使用 jQuery.event - globalEventContext = callbackContext!== s && - ( callbackContext.nodeType || callbackContext instanceof jQuery )? - jQuery( callbackContext ) : jQuery.event, - // 创建 Deferred 对象 - deferred = jQuery.Deferred(), - // 仅调用一次的回调函数 - completeDeferred = jQuery.Callbacks( "once memory" ), - // 状态相关的回调函数 - statusCode = s.statusCode || {}, - // ifModified 键 - ifModifiedKey, - // 请求头,一次性发送 - requestHeaders = {}, - requestHeadersNames = {}, - // 响应头 - responseHeadersString, - responseHeaders, - // 传输对象 - transport, - // 超时处理的定时器 - timeoutTimer, - // 跨域检测变量 - parts, - // jqXHR 的状态 - state = 0, - // 是否触发全局事件 - fireGlobals, - // 循环变量 - i, - // 模拟的 xhr 对象 - jqXHR = { - readyState: 0, - // 设置请求头 - setRequestHeader: function( name, value ) { - if (!state ) { - var lname = name.toLowerCase(); - name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; - requestHeaders[ name ] = value; - } - return this; - }, - // 获取所有响应头 - getAllResponseHeaders: function() { - return state === 2? responseHeadersString : null; - }, - // 构建响应头的哈希表(如果需要) - getResponseHeader: function( key ) { - var match; - if ( state === 2 ) { - if (!responseHeaders ) { - responseHeaders = {}; - while( ( match = rheaders.exec( responseHeadersString ) ) ) { - responseHeaders[ match[1].toLowerCase() ] = match[ 2 ]; - } + jQuery(function() { + // This hook cannot be added until DOM ready because the support test + // for it is not run until after DOM ready + if ( !jQuery.support.reliableMarginRight ) { + jQuery.cssHooks.marginRight = { + get: function( elem, computed ) { + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + // Work around by temporarily setting element display to inline-block + return jQuery.swap( elem, { "display": "inline-block" }, function() { + if ( computed ) { + return curCSS( elem, "margin-right" ); + } else { + return elem.style.marginRight; } - match = responseHeaders[ key.toLowerCase() ]; - } - return match === undefined? null : match; - }, - // 覆盖响应的内容类型头 - overrideMimeType: function( type ) { - if (!state ) { - s.mimeType = type; - } - return this; - }, - // 取消请求 - abort: function( statusText ) { - statusText = statusText || "abort"; - if ( transport ) { - transport.abort( statusText ); - } - done( 0, statusText ); - return this; + }); } }; + } + }); - // Callback for when everything is done - // It is defined here because jslint complains if it is declared - // at the end of the function (which would be more logical and readable) - function done( status, nativeStatusText, responses, headers ) { - // Called once - if ( state === 2 ) { - // 如果状态等于 2,则直接返回 - return; - } - - // State is "done" now - state = 2; + if ( jQuery.expr && jQuery.expr.filters ) { + jQuery.expr.filters.hidden = function( elem ) { + var width = elem.offsetWidth, + height = elem.offsetHeight; - // Clear timeout if it exists - if ( timeoutTimer ) { - // 如果存在超时定时器,则清除它 - clearTimeout( timeoutTimer ); - } + return ( width === 0 && height === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none"); + }; - // Dereference transport for early garbage collection - // (no matter how long the jqXHR object will be used) - transport = undefined; + jQuery.expr.filters.visible = function( elem ) { + return !jQuery.expr.filters.hidden( elem ); + }; + } - // Cache response headers - responseHeadersString = headers || ""; +// These hooks are used by animate to expand properties + jQuery.each({ + margin: "", + padding: "", + border: "Width" + }, function( prefix, suffix ) { - // Set readyState - jqXHR.readyState = status > 0? 4 : 0; + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i, - var isSuccess, - success, - error, - statusText = nativeStatusText, - response = responses? ajaxHandleResponses( s, jqXHR, responses ) : undefined, - lastModified, - etag; + // assumes a single number if not a string + parts = typeof value === "string" ? value.split(" ") : [ value ], + expanded = {}; - // If successful, handle type chaining - if ( status >= 200 && status < 300 || status === 304 ) { - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) { - // 如果存在 Last-Modified 响应头,将其存储在 jQuery.lastModified 对象中 - jQuery.lastModified[ ifModifiedKey ] = lastModified; - } - if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) { - // 如果存在 Etag 响应头,将其存储在 jQuery.etag 对象中 - jQuery.etag[ ifModifiedKey ] = etag; - } + for ( i = 0; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; } - // If not modified - if ( status === 304 ) { - statusText = "notmodified"; - isSuccess = true; - } else { - // If we have data - try { - // 尝试将响应转换为相应的数据格式 - success = ajaxConvert( s, response ); - statusText = "success"; - isSuccess = true; - } catch(e) { - // We have a parsererror - statusText = "parsererror"; - error = e; - } - } - } else { - // We extract error from statusText - // then normalize statusText and status for non-aborts - error = statusText; - if (!statusText || status ) { - statusText = "error"; - if ( status < 0 ) { - status = 0; - } - } + return expanded; } + }; + }); - // Set data for the fake xhr object - jqXHR.status = status; - jqXHR.statusText = "" + ( nativeStatusText || statusText ); - // Success/Error - if ( isSuccess ) { - // 若请求成功,使用 deferred 对象的 resolveWith 方法解决承诺 - deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); - } else { - // 若请求失败,使用 deferred 对象的 rejectWith 方法拒绝承诺 - deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); - } - // Status-dependent callbacks - jqXHR.statusCode( statusCode ); - statusCode = undefined; - if ( fireGlobals ) { - // 触发全局的 ajax 成功或失败事件 - globalEventContext.trigger( "ajax" + ( isSuccess? "Success" : "Error" ), - [ jqXHR, s, isSuccess? success : error ] ); - } + var r20 = /%20/g, + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rhash = /#.*$/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL + rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i, + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + rquery = /\?/, + rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, + rselectTextarea = /^(?:select|textarea)/i, + rspacesAjax = /\s+/, + rts = /([?&])_=[^&]*/, + rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/, + + // Keep a copy of the old load method + _load = jQuery.fn.load, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Document location + ajaxLocation, + + // Document location segments + ajaxLocParts, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = ["*/"] + ["*"]; + +// #8138, IE may throw an exception when accessing +// a field from window.location if document.domain has been set + try { + ajaxLocation = location.href; + } catch( e ) { + // Use the href attribute of an A element + // since IE will modify it given document.location + ajaxLocation = document.createElement( "a" ); + ajaxLocation.href = ""; + ajaxLocation = ajaxLocation.href; + } - // Complete - completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); +// Segment location into parts + ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; - if ( fireGlobals ) { - // 触发全局的 ajax 完成事件 - globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); - // Handle the global AJAX counter - if (!( --jQuery.active ) ) { - // 如果 jQuery.active 减为 0,触发 ajaxStop 事件 - jQuery.event.trigger( "ajaxStop" ); - } +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport + function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; } - } -// Attach deferreds - deferred.promise( jqXHR ); - jqXHR.success = jqXHR.done; - jqXHR.error = jqXHR.fail; - jqXHR.complete = completeDeferred.add; - -// Status-dependent callbacks - jqXHR.statusCode = function( map ) { - if ( map ) { - var tmp; - if ( state < 2 ) { - for ( tmp in map ) { - // 存储状态码映射关系 - statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ]; + if ( jQuery.isFunction( func ) ) { + var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ), + i = 0, + length = dataTypes.length, + dataType, + list, + placeBefore; + + // For each dataType in the dataTypeExpression + for ( ; i < length; i++ ) { + dataType = dataTypes[ i ]; + // We control if we're asked to add before + // any existing element + placeBefore = /^\+/.test( dataType ); + if ( placeBefore ) { + dataType = dataType.substr( 1 ) || "*"; } - } else { - tmp = map[ jqXHR.status ]; - // 根据状态码添加相应的 then 处理 - jqXHR.then( tmp, tmp ); + list = structure[ dataType ] = structure[ dataType ] || []; + // then we add to the structure accordingly + list[ placeBefore ? "unshift" : "push" ]( func ); } } - return this; }; + } -// Remove hash character (#7531: and string promotion) -// Add protocol if not provided (#5866: IE7 issue with protocol-less urls) -// We also use the url parameter if available - s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); - -// Extract dataTypes list - s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax ); - -// Determine if a cross-domain request is in order - if ( s.crossDomain == null ) { - parts = rurl.exec( s.url.toLowerCase() ); - s.crossDomain =!!( parts && - ( parts[ 1 ]!= ajaxLocParts[ 1 ] || parts[ 2 ]!= ajaxLocParts[ 2 ] || - ( parts[ 3 ] || ( parts[ 1 ] === "http:"? 80 : 443 ) )!= - ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:"? 80 : 443 ) ) ) - ); +// Base inspection function for prefilters and transports + function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR, + dataType /* internal */, inspected /* internal */ ) { + + dataType = dataType || options.dataTypes[ 0 ]; + inspected = inspected || {}; + + inspected[ dataType ] = true; + + var list = structure[ dataType ], + i = 0, + length = list ? list.length : 0, + executeOnly = ( structure === prefilters ), + selection; + + for ( ; i < length && ( executeOnly || !selection ); i++ ) { + selection = list[ i ]( options, originalOptions, jqXHR ); + // If we got redirected to another dataType + // we try there if executing only and not done already + if ( typeof selection === "string" ) { + if ( !executeOnly || inspected[ selection ] ) { + selection = undefined; + } else { + options.dataTypes.unshift( selection ); + selection = inspectPrefiltersOrTransports( + structure, options, originalOptions, jqXHR, selection, inspected ); + } + } } - -// Convert data if not already a string - if ( s.data && s.processData && typeof s.data!== "string" ) { - // 将数据转换为查询字符串形式 - s.data = jQuery.param( s.data, s.traditional ); + // If we're only executing or nothing was selected + // we try the catchall dataType if not done already + if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) { + selection = inspectPrefiltersOrTransports( + structure, options, originalOptions, jqXHR, "*", inspected ); } + // unnecessary when only executing (prefilters) + // but it'll be ignored by the caller in that case + return selection; + } -// Apply prefilters - inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); - -// If request was aborted inside a prefilter, stop there - if ( state === 2 ) { - // 如果在预过滤器中请求被中止,则返回 false - return false; +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 + function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } } - -// We can fire global events as of now if asked to - fireGlobals = s.global; - -// Uppercase the type - s.type = s.type.toUpperCase(); - -// Determine if request has content - s.hasContent =!rnoContent.test( s.type ); - -// Watch for a new set of requests - if ( fireGlobals && jQuery.active++ === 0 ) { - // 如果允许触发全局事件且 jQuery.active 为 0,则触发 ajaxStart 事件 - jQuery.event.trigger( "ajaxStart" ); + if ( deep ) { + jQuery.extend( true, target, deep ); } + } -// More options handling for requests with no content - if (!s.hasContent ) { - // If data is available, append data to url - if ( s.data ) { - s.url += ( rquery.test( s.url )? "&" : "?" ) + s.data; - // #9682: remove data so that it's not used in an eventual retry - delete s.data; - } - - // Get ifModifiedKey before adding the anti-cache parameter - ifModifiedKey = s.url; - - // Add anti-cache in url if needed - if ( s.cache === false ) { - var ts = jQuery.now(), - // try replacing _= if it is there - ret = s.url.replace( rts, "$1_=" + ts ); + jQuery.fn.extend({ + load: function( url, params, callback ) { + if ( typeof url !== "string" && _load ) { + return _load.apply( this, arguments ); - // if nothing was replaced, add timestamp to the end - s.url = ret + ( ( ret === s.url )? ( rquery.test( s.url )? "&" : "?" ) + "_=" + ts : "" ); + // Don't do a request if no elements are being requested + } else if ( !this.length ) { + return this; } - } - -// Set the correct header, if data is being sent - if ( s.data && s.hasContent && s.contentType!== false || options.contentType ) { - // 设置请求的 Content-Type 头 - jqXHR.setRequestHeader( "Content-Type", s.contentType ); - } -// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - ifModifiedKey = ifModifiedKey || s.url; - if ( jQuery.lastModified[ ifModifiedKey ] ) { - // 设置 If-Modified-Since 头 - jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] ); - } - if ( jQuery.etag[ ifModifiedKey ] ) { - // 设置 If-None-Match 头 - jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] ); - } - } + var off = url.indexOf( " " ); + if ( off >= 0 ) { + var selector = url.slice( off, url.length ); + url = url.slice( 0, off ); + } + + // Default to a GET request + var type = "GET"; + + // If the second parameter was provided + if ( params ) { + // If it's a function + if ( jQuery.isFunction( params ) ) { + // We assume that it's the callback + callback = params; + params = undefined; + + // Otherwise, build a param string + } else if ( typeof params === "object" ) { + params = jQuery.param( params, jQuery.ajaxSettings.traditional ); + type = "POST"; + } + } + + var self = this; + + // Request the remote document + jQuery.ajax({ + url: url, + type: type, + dataType: "html", + data: params, + // Complete callback (responseText is used internally) + complete: function( jqXHR, status, responseText ) { + // Store the response as specified by the jqXHR object + responseText = jqXHR.responseText; + // If successful, inject the HTML into all the matched elements + if ( jqXHR.isResolved() ) { + // #4825: Get the actual response in case + // a dataFilter is present in ajaxSettings + jqXHR.done(function( r ) { + responseText = r; + }); + // See if a selector was specified + self.html( selector ? + // Create a dummy div to hold the results + jQuery("<div>") + // inject the contents of the document in, removing the scripts + // to avoid any 'Permission Denied' errors in IE + .append(responseText.replace(rscript, "")) - // Set the Accepts header for the server, depending on the dataType - jqXHR.setRequestHeader( - "Accept", - s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ]? - s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ]!== "*"? ", " + allTypes + "; q=0.01" : "" ) : - s.accepts[ "*" ] - ); -// 设置请求的 Accept 头信息,根据 dataTypes 和 accepts 属性进行设置 - -// Check for headers option - for ( i in s.headers ) { - // 遍历 s.headers 并设置请求头信息 - jqXHR.setRequestHeader( i, s.headers[ i ] ); - } + // Locate the specified elements + .find(selector) : -// Allow custom headers/mimetypes and early abort - if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { - // 如果 beforeSend 回调返回 false 或者状态为 2,则中止请求 - // Abort if not done already - jqXHR.abort(); - return false; - } + // If not, just inject the full result + responseText ); + } -// Install callbacks on deferreds - for ( i in { success: 1, error: 1, complete: 1 } ) { - // 为 success、error、complete 事件安装回调函数 - jqXHR[ i ]( s[ i ] ); - } + if ( callback ) { + self.each( callback, [ responseText, status, jqXHR ] ); + } + } + }); -// Get transport - transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); -// 获取传输对象 + return this; + }, -// If no transport, we auto-abort - if (!transport ) { - // 如果没有传输对象,完成请求并设置状态为 -1 及相应的错误信息 - done( -1, "No Transport" ); - } else { - jqXHR.readyState = 1; - // Send global event - if ( fireGlobals ) { - // 发送全局的 ajaxSend 事件 - globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); - } - // Timeout - if ( s.async && s.timeout > 0 ) { - // 设置超时定时器,如果超时则中止请求 - timeoutTimer = setTimeout( function(){ - jqXHR.abort( "timeout" ); - }, s.timeout ); - } + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, - try { - state = 1; - // 发送请求,并将 done 作为完成回调函数 - transport.send( requestHeaders, done ); - } catch (e) { - // Propagate exception as error if not done - if ( state < 2 ) { - // 如果状态小于 2,将异常作为错误完成请求 - done( -1, e ); - // Simply rethrow otherwise - } else { - // 否则重新抛出异常 - throw e; - } - } + serializeArray: function() { + return this.map(function(){ + return this.elements ? jQuery.makeArray( this.elements ) : this; + }) + .filter(function(){ + return this.name && !this.disabled && + ( this.checked || rselectTextarea.test( this.nodeName ) || + rinput.test( this.type ) ); + }) + .map(function( i, elem ){ + var val = jQuery( this ).val(); + + return val == null ? + null : + jQuery.isArray( val ) ? + jQuery.map( val, function( val, i ){ + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }) : + { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }).get(); } + }); - return jqXHR; - }, - -// Serialize an array of form elements or a set of -// key/values into a query string - param: function( a, traditional ) { - var s = [], - add = function( key, value ) { - // If value is a function, invoke it and return its value - value = jQuery.isFunction( value )? value() : value; - // 将键值对添加到数组 s 中,并进行 URI 编码 - s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value ); - }; +// Attach a bunch of functions for handling common AJAX events + jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){ + jQuery.fn[ o ] = function( f ){ + return this.on( o, f ); + }; + }); - // Set traditional to true for jQuery <= 1.3.2 behavior. - if ( traditional === undefined ) { - traditional = jQuery.ajaxSettings.traditional; - } + jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + // shift arguments if data argument was omitted + if ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } - // If an array was passed in, assume that it is an array of form elements. - if ( jQuery.isArray( a ) || ( a.jquery &&!jQuery.isPlainObject( a ) ) ) { - // Serialize the form elements - jQuery.each( a, function() { - // 遍历数组或 jQuery 对象,调用 add 函数添加键值对 - add( this.name, this.value ); + return jQuery.ajax({ + type: method, + url: url, + data: data, + success: callback, + dataType: type }); + }; + }); - } else { - // If traditional, encode the "old" way (the way 1.3.2 or older - // did it), otherwise encode params recursively. - for ( var prefix in a ) { - // 对于对象,调用 buildParams 进行参数构建 - buildParams( prefix, a[ prefix ], traditional, add ); - } - } + jQuery.extend({ - // Return the resulting serialization - return s.join( "&" ).replace( r20, "+" ); - // 将数组元素拼接为查询字符串并替换相应字符 - } -}); + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + }, -function buildParams( prefix, obj, traditional, add ) { - if ( jQuery.isArray( obj ) ) { - // Serialize array item. - jQuery.each( obj, function( i, v ) { - if ( traditional || rbracket.test( prefix ) ) { - // Treat each array item as a scalar. - // 将数组元素作为标量添加 - add( prefix, v ); + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + if ( settings ) { + // Building a settings object + ajaxExtend( target, jQuery.ajaxSettings ); } else { - // If array item is non-scalar (array or object), encode its - // numeric index to resolve deserialization ambiguity issues. - // Note that rack (as of 1.0.0) can't currently deserialize - // nested arrays properly, and attempting to do so may cause - // a server error. Possible fixes are to modify rack's - // deserialization algorithm or to provide an option or flag - // to force array serialization to be shallow. - // 对于非标量数组元素,递归调用 buildParams 进行编码 - buildParams( prefix + "[" + ( typeof v === "object"? i : "" ) + "]", v, traditional, add ); + // Extending ajaxSettings + settings = target; + target = jQuery.ajaxSettings; } - }); + ajaxExtend( target, settings ); + return target; + }, - } else if (!traditional && jQuery.type( obj ) === "object" ) { - // Serialize object item. - for ( var name in obj ) { - // 对于对象,递归调用 buildParams 进行编码 - buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); - } + ajaxSettings: { + url: ajaxLocation, + isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ), + global: true, + type: "GET", + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + processData: true, + async: true, + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + traditional: false, + headers: {}, + */ + + accepts: { + xml: "application/xml, text/xml", + html: "text/html", + text: "text/plain", + json: "application/json, text/javascript", + "*": allTypes + }, - } else { - // Serialize scalar item. - // 对于标量,直接添加 - add( prefix, obj ); - } -} + contents: { + xml: /xml/, + html: /html/, + json: /json/ + }, -// This is still on the jQuery object... for now -// Want to move this to jQuery.ajax some day -jQuery.extend({ + responseFields: { + xml: "responseXML", + text: "responseText" + }, - // Counter for holding the number of active queries - active: 0, - // 存储活动请求的数量 + // List of data converters + // 1) key format is "source_type destination_type" (a single space in-between) + // 2) the catchall symbol "*" can be used for source_type + converters: { - // Last-Modified header cache for next request - lastModified: {}, - etag: {} - // 存储 Last-Modified 和 Etag 头信息 -}); + // Convert anything to text + "* text": window.String, -/* Handles responses to an ajax request: - * - sets all responseXXX fields accordingly - * - finds the right dataType (mediates between content-type and expected dataType) - * - returns the corresponding response - */ -function ajaxHandleResponses( s, jqXHR, responses ) { - - var contents = s.contents, - dataTypes = s.dataTypes, - responseFields = s.responseFields, - ct, - type, - finalDataType, - firstDataType; - - // Fill responseXXX fields - for ( type in responseFields ) { - if ( type in responses ) { - // 填充响应字段 - jqXHR[ responseFields[type] ] = responses[ type ]; - } - } + // Text to html (true = no transformation) + "text html": true, - // Remove auto dataType and get content-type in the process - while( dataTypes[ 0 ] === "*" ) { - dataTypes.shift(); - if ( ct === undefined ) { - // 获取响应的 content-type 头信息 - ct = s.mimeType || jqXHR.getResponseHeader( "content-type" ); - } - } + // Evaluate text as a json expression + "text json": jQuery.parseJSON, - // Check if we're dealing with a known content-type - if ( ct ) { - for ( type in contents ) { - if ( contents[ type ] && contents[ type ].test( ct ) ) { - // 根据 content-type 找到对应的 dataType 并添加到 dataTypes 头部 - dataTypes.unshift( type ); - break; - } - } - } + // Parse text as xml + "text xml": jQuery.parseXML + }, - // 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; - break; - } - if (!firstDataType ) { - firstDataType = type; + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + context: true, + url: true } - } - // 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 ); - } - return responses[ finalDataType ]; - // 返回最终的数据类型的响应 - } -} + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + // Callbacks context + callbackContext = s.context || s, + // Context for global events + // It's the callbackContext if one was provided in the options + // and if it's a DOM node or a jQuery collection + globalEventContext = callbackContext !== s && + ( callbackContext.nodeType || callbackContext instanceof jQuery ) ? + jQuery( callbackContext ) : jQuery.event, + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + // Status-dependent callbacks + statusCode = s.statusCode || {}, + // ifModified key + ifModifiedKey, + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + // Response headers + responseHeadersString, + responseHeaders, + // transport + transport, + // timeout handle + timeoutTimer, + // Cross-domain detection vars + parts, + // The jqXHR state + state = 0, + // To know if global events are to be dispatched + fireGlobals, + // Loop variable + i, + // Fake xhr + jqXHR = { -// Chain conversions given the request and the original response -function ajaxConvert( s, response ) { + readyState: 0, - // Apply the dataFilter if provided - if ( s.dataFilter ) { - // 应用数据过滤器 - response = s.dataFilter( response, s.dataType ); - } + // Caches the header + setRequestHeader: function( name, value ) { + if ( !state ) { + var lname = name.toLowerCase(); + name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; + requestHeaders[ name ] = value; + } + return this; + }, - var dataTypes = s.dataTypes, - converters = {}, - i, - 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" ) { - // 创建转换器映射 - converters[ key.toLowerCase() ] = s.converters[ key ]; - } - } - } + // Raw string + getAllResponseHeaders: function() { + return state === 2 ? responseHeadersString : null; + }, - // Get the dataTypes - prev = current; - current = dataTypes[ i ]; - - // If current is auto dataType, update it to 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 ) { - tmp = conv1.split( " " ); - if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) { - conv2 = converters[ tmp[1] + " " + current ]; - if ( conv2 ) { - conv1 = converters[ conv1 ]; - if ( conv1 === true ) { - conv = conv2; - } else if ( conv2 === true ) { - conv = conv1; + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( state === 2 ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[1].toLowerCase() ] = match[ 2 ]; + } } - break; + match = responseHeaders[ key.toLowerCase() ]; } - } - } - } - // 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 - response = conv? conv( response ) : conv2( conv1(response) ); - // 使用转换器进行数据转换 - } - } - } - return response; -} + return match === undefined ? null : match; + }, + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( !state ) { + s.mimeType = type; + } + return this; + }, + // Cancel the request + abort: function( statusText ) { + statusText = statusText || "abort"; + if ( transport ) { + transport.abort( statusText ); + } + done( 0, statusText ); + return this; + } + }; + // Callback for when everything is done + // It is defined here because jslint complains if it is declared + // at the end of the function (which would be more logical and readable) + function done( status, nativeStatusText, responses, headers ) { -// 创建一个变量存储 jQuery 的当前时间戳 -var jsc = jQuery.now(), - // 定义一个正则表达式,用于匹配特定的字符串模式 - jsre = /(\=)\?(&|$)|\?\?/i; + // Called once + if ( state === 2 ) { + return; + } -// Default jsonp settings -// 设置 jQuery 的 AJAX 默认配置 -jQuery.ajaxSetup({ - // jsonp 请求的回调函数名称参数 - jsonp: "callback", - jsonpCallback: function() { - // 生成一个唯一的 jsonp 回调函数名称,基于 jQuery 的 expando 属性和时间戳 - return jQuery.expando + "_" + (jsc++); - } -}); + // State is "done" now + state = 2; -// Detect, normalize options and install callbacks for jsonp requests -// 为 jsonp 请求检测、规范化选项和安装回调函数 -jQuery.ajaxPrefilter("json jsonp", function(s, originalSettings, jqXHR) { - // 检查 s.data 是否为字符串且其 contentType 是否为 application/x-www-form-urlencoded 类型 - var inspectData = (typeof s.data === "string") && /^application\/x\-www\-form\-urlencoded/.test(s.contentType); - - if (s.dataTypes[0] === "jsonp" || - s.jsonp!== false && (jsre.test(s.url) || - inspectData && jsre.test(s.data))) { - - var responseContainer, - // 确定 jsonpCallback 的值,根据 s.jsonpCallback 是否为函数调用相应的方法 - jsonpCallback = s.jsonpCallback = - jQuery.isFunction(s.jsonpCallback)? s.jsonpCallback() : s.jsonpCallback, - // 存储之前的 window[jsonpCallback] 函数 - previous = window[jsonpCallback], - url = s.url, - data = s.data, - // 用于替换的字符串模式 - replace = "$1" + jsonpCallback + "$2"; - - if (s.jsonp!== false) { - // 替换 url 中的匹配部分 - url = url.replace(jsre, replace); - if (s.url === url) { - if (inspectData) { - // 替换 data 中的匹配部分 - data = data.replace(jsre, replace); - } - if (s.data === data) { - // 如果 url 和 data 都没有变化,手动添加回调参数 - url += (/\?/.test(url)? "&" : "?") + s.jsonp + "=" + jsonpCallback; + // Clear timeout if it exists + if ( timeoutTimer ) { + clearTimeout( timeoutTimer ); } - } - } - s.url = url; - s.data = data; + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; - // Install callback - // 在 window 对象上安装回调函数,将响应存储在 responseContainer 中 - window[jsonpCallback] = function(response) { - responseContainer = [response]; - }; + // Cache response headers + responseHeadersString = headers || ""; - // Clean-up function - jqXHR.always(function() { - // 将回调函数恢复到之前的值 - window[jsonpCallback] = previous; - // 如果是函数且有响应,调用该回调函数 - if (responseContainer && jQuery.isFunction(previous)) { - window[jsonpCallback](responseContainer[0]); - } - }); + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; - // Use data converter to retrieve json after script execution - // 定义数据转换器,当脚本执行后尝试获取 json 数据 - s.converters["script json"] = function() { - if (!responseContainer) { - // 如果没有调用回调函数则报错 - jQuery.error(jsonpCallback + " was not called"); - } - return responseContainer[0]; - }; + var isSuccess, + success, + error, + statusText = nativeStatusText, + response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined, + lastModified, + etag; - // force json dataType - // 强制将第一个数据类型设置为 json - s.dataTypes[0] = "json"; + // If successful, handle type chaining + if ( status >= 200 && status < 300 || status === 304 ) { - // Delegate to script - // 委托给 script 处理 - return "script"; - } -}); + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) { + jQuery.lastModified[ ifModifiedKey ] = lastModified; + } + if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) { + jQuery.etag[ ifModifiedKey ] = etag; + } + } -// Install script dataType -// 安装 script 数据类型的相关配置 -jQuery.ajaxSetup({ - accepts: { - // 设置 script 类型的接受内容类型 - script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" - }, - contents: { - // 定义 script 类型的内容匹配规则 - script: /javascript|ecmascript/ - }, - converters: { - // 定义 text 到 script 的数据转换器,使用 jQuery.globalEval 执行脚本 - "text script": function(text) { - jQuery.globalEval(text); - return text; - } - } -}); + // If not modified + if ( status === 304 ) { -// Handle cache's special case and global -// 处理 script 类型的缓存特殊情况和全局设置 -jQuery.ajaxPrefilter("script", function(s) { - if (s.cache === undefined) { - // 如果缓存未定义,设置为 false - s.cache = false; - } - if (s.crossDomain) { - // 如果是跨域请求,设置请求类型为 GET 并关闭全局设置 - s.type = "GET"; - s.global = false; - } -}); + statusText = "notmodified"; + isSuccess = true; -// Bind script tag hack transport -// 绑定脚本标签的传输方式 -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"; - - if (s.scriptCharset) { - // 设置 script 元素的字符集 - script.charset = s.scriptCharset; - } - - script.src = s.url; - - // Attach handlers for all browsers - // 为 script 元素添加加载和状态改变的事件处理函数 - script.onload = script.onreadystatechange = function(_, isAbort) { - 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); + // If we have data + } else { + + try { + success = ajaxConvert( s, response ); + statusText = "success"; + isSuccess = true; + } catch(e) { + // We have a parsererror + statusText = "parsererror"; + error = e; + } + } + } else { + // We extract error from statusText + // then normalize statusText and status for non-aborts + error = statusText; + if ( !statusText || status ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = "" + ( nativeStatusText || statusText ); - // Dereference the script - // 解除对 script 的引用 - script = undefined; + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ), + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); - // Callback if not abort - // 如果没有中止,调用回调函数 - if (!isAbort) { - callback(200, "success"); + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + // Attach deferreds + deferred.promise( jqXHR ); + jqXHR.success = jqXHR.done; + jqXHR.error = jqXHR.fail; + jqXHR.complete = completeDeferred.add; + + // Status-dependent callbacks + jqXHR.statusCode = function( map ) { + if ( map ) { + var tmp; + if ( state < 2 ) { + for ( tmp in map ) { + statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ]; } + } else { + tmp = map[ jqXHR.status ]; + jqXHR.then( tmp, tmp ); + } + } + return this; + }; + + // Remove hash character (#7531: and string promotion) + // Add protocol if not provided (#5866: IE7 issue with protocol-less urls) + // We also use the url parameter if available + s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); + + // Extract dataTypes list + s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax ); + + // Determine if a cross-domain request is in order + if ( s.crossDomain == null ) { + parts = rurl.exec( s.url.toLowerCase() ); + s.crossDomain = !!( parts && + ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] || + ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) != + ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) ) + ); + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( state === 2 ) { + return false; + } + + // We can fire global events as of now if asked to + fireGlobals = s.global; + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // If data is available, append data to url + if ( s.data ) { + s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data; + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Get ifModifiedKey before adding the anti-cache parameter + ifModifiedKey = s.url; + + // Add anti-cache in url if needed + if ( s.cache === false ) { + + var ts = jQuery.now(), + // try replacing _= if it is there + ret = s.url.replace( rts, "$1_=" + ts ); + + // if nothing was replaced, add timestamp to the end + s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + ifModifiedKey = ifModifiedKey || s.url; + if ( jQuery.lastModified[ ifModifiedKey ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] ); + } + if ( jQuery.etag[ ifModifiedKey ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] ); + } + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ? + s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { + // Abort if not done already + jqXHR.abort(); + return false; + + } + + // Install callbacks on deferreds + for ( i in { success: 1, error: 1, complete: 1 } ) { + jqXHR[ i ]( s[ i ] ); + } + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = setTimeout( function(){ + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + state = 1; + transport.send( requestHeaders, done ); + } catch (e) { + // Propagate exception as error if not done + if ( state < 2 ) { + done( -1, e ); + // Simply rethrow otherwise + } else { + throw e; } + } + } + + return jqXHR; + }, + + // Serialize an array of form elements or a set of + // key/values into a query string + param: function( a, traditional ) { + var s = [], + add = function( key, value ) { + // If value is a function, invoke it and return its value + value = jQuery.isFunction( value ) ? value() : value; + s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value ); }; - // Use insertBefore instead of appendChild to circumvent an IE6 bug. - // 使用 insertBefore 代替 appendChild 来避免 IE6 的一个 bug - // This arises when a base node is used (#2709 and #4378). - // 当使用 base 节点时会出现该问题(参考问题 #2709 和 #4378) - head.insertBefore(script, head.firstChild); - }, - abort: function() { - if (script) { - // 中止请求 - script.onload(0, 1); + // Set traditional to true for jQuery <= 1.3.2 behavior. + if ( traditional === undefined ) { + traditional = jQuery.ajaxSettings.traditional; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + }); + + } else { + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( var prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); } } - }; + + // Return the resulting serialization + return s.join( "&" ).replace( r20, "+" ); + } + }); + + function buildParams( prefix, obj, traditional, add ) { + if ( jQuery.isArray( obj ) ) { + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + // If array item is non-scalar (array or object), encode its + // numeric index to resolve deserialization ambiguity issues. + // Note that rack (as of 1.0.0) can't currently deserialize + // nested arrays properly, and attempting to do so may cause + // a server error. Possible fixes are to modify rack's + // deserialization algorithm or to provide an option or flag + // to force array serialization to be shallow. + buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add ); + } + }); + + } else if ( !traditional && jQuery.type( obj ) === "object" ) { + // Serialize object item. + for ( var name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + // Serialize scalar item. + add( prefix, obj ); + } } -}); +// This is still on the jQuery object... for now +// Want to move this to jQuery.ajax some day + jQuery.extend({ + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {} + + }); + /* Handles responses to an ajax request: + * - sets all responseXXX fields accordingly + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ + function ajaxHandleResponses( s, jqXHR, responses ) { + + var contents = s.contents, + dataTypes = s.dataTypes, + responseFields = s.responseFields, + ct, + type, + finalDataType, + firstDataType; -var // #5280: Internet Explorer will keep connections alive if we don't abort on unload - // 如果在卸载时不中止,IE 会保持连接处于活动状态 - xhrOnUnloadAbort = window.ActiveXObject? function() { - // Abort all pending requests - // 中止所有挂起的请求 - for ( var key in xhrCallbacks ) { - xhrCallbacks[ key ]( 0, 1 ); + // Fill responseXXX fields + for ( type in responseFields ) { + if ( type in responses ) { + jqXHR[ responseFields[type] ] = responses[ type ]; + } } - } : false, - xhrId = 0, - xhrCallbacks; + + // Remove auto dataType and get content-type in the process + while( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "content-type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // 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; + break; + } + if ( !firstDataType ) { + 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 ); + } + return responses[ finalDataType ]; + } + } + +// 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 ); + } + + var dataTypes = s.dataTypes, + converters = {}, + i, + 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" ) { + converters[ key.toLowerCase() ] = s.converters[ key ]; + } + } + } + + // Get the dataTypes + prev = current; + current = dataTypes[ i ]; + + // If current is auto dataType, update it to 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 ) { + tmp = conv1.split( " " ); + if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) { + conv2 = converters[ tmp[1] + " " + current ]; + if ( conv2 ) { + conv1 = converters[ conv1 ]; + if ( conv1 === true ) { + conv = conv2; + } else if ( conv2 === true ) { + conv = conv1; + } + break; + } + } + } + } + // 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 + response = conv ? conv( response ) : conv2( conv1(response) ); + } + } + } + return response; + } + + + + + var jsc = jQuery.now(), + jsre = /(\=)\?(&|$)|\?\?/i; + +// Default jsonp settings + jQuery.ajaxSetup({ + jsonp: "callback", + jsonpCallback: function() { + return jQuery.expando + "_" + ( jsc++ ); + } + }); + +// Detect, normalize options and install callbacks for jsonp requests + jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { + + var inspectData = ( typeof s.data === "string" ) && /^application\/x\-www\-form\-urlencoded/.test( s.contentType ); + + if ( s.dataTypes[ 0 ] === "jsonp" || + s.jsonp !== false && ( jsre.test( s.url ) || + inspectData && jsre.test( s.data ) ) ) { + + var responseContainer, + jsonpCallback = s.jsonpCallback = + jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback, + previous = window[ jsonpCallback ], + url = s.url, + data = s.data, + replace = "$1" + jsonpCallback + "$2"; + + if ( s.jsonp !== false ) { + url = url.replace( jsre, replace ); + if ( s.url === url ) { + if ( inspectData ) { + data = data.replace( jsre, replace ); + } + if ( s.data === data ) { + // Add callback manually + url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback; + } + } + } + + 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 + if ( responseContainer && jQuery.isFunction( previous ) ) { + window[ jsonpCallback ]( responseContainer[ 0 ] ); + } + }); + + // Use data converter to retrieve json after script execution + s.converters["script json"] = function() { + if ( !responseContainer ) { + jQuery.error( jsonpCallback + " was not called" ); + } + return responseContainer[ 0 ]; + }; + + // force json dataType + s.dataTypes[ 0 ] = "json"; + + // Delegate to script + return "script"; + } + }); + + + + +// Install script dataType + jQuery.ajaxSetup({ + accepts: { + script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /javascript|ecmascript/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } + }); + +// Handle cache's special case and global + jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + s.global = false; + } + }); + +// Bind script tag hack transport + jQuery.ajaxTransport( "script", function(s) { + + // This transport only deals with cross domain requests + if ( s.crossDomain ) { + + var script, + head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement; + + return { + + send: function( _, callback ) { + + script = document.createElement( "script" ); + + script.async = "async"; + + if ( s.scriptCharset ) { + script.charset = s.scriptCharset; + } + + script.src = s.url; + + // Attach handlers for all browsers + script.onload = script.onreadystatechange = function( _, isAbort ) { + + if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) { + + // Handle memory leak in IE + script.onload = script.onreadystatechange = null; + + // Remove the script + if ( head && script.parentNode ) { + head.removeChild( script ); + } + + // Dereference the 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). + head.insertBefore( script, head.firstChild ); + }, + + abort: function() { + if ( script ) { + script.onload( 0, 1 ); + } + } + }; + } + }); + + + + + var // #5280: Internet Explorer will keep connections alive if we don't abort on unload + xhrOnUnloadAbort = window.ActiveXObject ? function() { + // Abort all pending requests + for ( var key in xhrCallbacks ) { + xhrCallbacks[ key ]( 0, 1 ); + } + } : false, + xhrId = 0, + xhrCallbacks; // Functions to create xhrs -// 创建 XMLHttpRequest 对象的函数 -function createStandardXHR() { - try { - // 尝试创建标准的 XMLHttpRequest 对象 - return new window.XMLHttpRequest(); - } catch( e ) {} -} + function createStandardXHR() { + try { + return new window.XMLHttpRequest(); + } catch( e ) {} + } -function createActiveXHR() { - try { - // 尝试创建 ActiveXObject 类型的 XMLHttpRequest 对象 - return new window.ActiveXObject( "Microsoft.XMLHTTP" ); - } catch( e ) {} -} + function createActiveXHR() { + try { + return new window.ActiveXObject( "Microsoft.XMLHTTP" ); + } catch( e ) {} + } // Create the request object // (This is still attached to ajaxSettings for backward compatibility) -// 创建请求对象(为了向后兼容,仍然附加到 ajaxSettings) -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. - */ - function() { - // 在 IE 中,如果不是本地请求,优先使用标准的 XMLHttpRequest 对象,否则使用 ActiveXObject - return!this.isLocal && createStandardXHR() || createActiveXHR(); - } : - // For all other browsers, use the standard XMLHttpRequest object - // 对于其他浏览器,使用标准的 XMLHttpRequest 对象 - createStandardXHR; + 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. + */ + function() { + return !this.isLocal && createStandardXHR() || createActiveXHR(); + } : + // For all other browsers, use the standard XMLHttpRequest object + createStandardXHR; // Determine support properties -// 确定支持的属性 -(function( xhr ) { - jQuery.extend( jQuery.support, { - // 检查是否支持 ajax - ajax:!!xhr, - // 检查是否支持跨域资源共享 (CORS) - cors:!!xhr && ( "withCredentials" in xhr ) - }); -})( jQuery.ajaxSettings.xhr() ); + (function( xhr ) { + jQuery.extend( jQuery.support, { + ajax: !!xhr, + cors: !!xhr && ( "withCredentials" in xhr ) + }); + })( jQuery.ajaxSettings.xhr() ); // Create transport if the browser can provide an xhr -// 如果浏览器可以提供 xhr,则创建传输对象 -if ( jQuery.support.ajax ) { + if ( jQuery.support.ajax ) { - jQuery.ajaxTransport(function( s ) { - // Cross domain only allowed if supported through XMLHttpRequest - // 仅当通过 XMLHttpRequest 支持跨域时允许跨域请求 - if (!s.crossDomain || jQuery.support.cors ) { + jQuery.ajaxTransport(function( s ) { + // Cross domain only allowed if supported through XMLHttpRequest + if ( !s.crossDomain || jQuery.support.cors ) { - var callback; + 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 ); - } + return { + send: function( headers, complete ) { - // Apply custom fields if provided - // 如果提供了自定义字段,则应用它们 - if ( s.xhrFields ) { - for ( i in s.xhrFields ) { - xhr[ i ] = s.xhrFields[ i ]; + // Get a new xhr + var xhr = s.xhr(), + handle, + i; + + // Open the socket + // Passing null username, generates a login popup on 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 ); } - } - // Override mime type if needed - // 如果需要,覆盖 MIME 类型 - if ( s.mimeType && xhr.overrideMimeType ) { - xhr.overrideMimeType( s.mimeType ); - } + // Apply custom fields if provided + if ( s.xhrFields ) { + for ( i in s.xhrFields ) { + xhr[ i ] = s.xhrFields[ i ]; + } + } - // X-Requested-With header - // 对于跨域请求,由于预检条件比较复杂,所以不设置该请求头 - // 对于同域请求,如果未提供该请求头,则设置 - // (也可以在每次请求时设置或使用 ajaxSetup 设置) - // 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"; - } + // Override mime type if needed + if ( s.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( s.mimeType ); + } - // Need an extra try/catch for cross domain requests in Firefox 3 - // Firefox 3 中的跨域请求需要额外的 try/catch 处理 - try { - for ( i in headers ) { - xhr.setRequestHeader( i, headers[ i ] ); + // 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"; } - } catch( _ ) {} - - // Do send the request - // 发送请求 - // 这可能会引发异常,实际上 jQuery.ajax 会处理该异常,所以这里没有 try/catch - // This may raise an exception which is actually - // handled in jQuery.ajax (so no try/catch here) - 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) + + // Need an extra try/catch for cross domain requests in Firefox 3 try { + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + } catch( _ ) {} - // Was never called and is aborted or complete - // 如果未调用过且已中止或完成 - if ( callback && ( isAbort || xhr.readyState === 4 ) ) { + // Do send the request + // This may raise an exception which is actually + // handled in jQuery.ajax (so no try/catch here) + xhr.send( ( s.hasContent && s.data ) || null ); - // Only called once - // 只调用一次 - callback = undefined; + // Listener + callback = function( _, isAbort ) { - // Do not keep as active anymore - // 不再保持活动状态 - if ( handle ) { - xhr.onreadystatechange = jQuery.noop; - if ( xhrOnUnloadAbort ) { - delete xhrCallbacks[ handle ]; - } - } + var status, + statusText, + responseHeaders, + responses, + xml; - // 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 = xhr.responseXML; - - // Construct response list - // 构建响应列表 - if ( xml && xml.documentElement /* #4958 */ ) { - responses.xml = xml; - } + // Firefox throws exceptions when accessing properties + // of an xhr when a network error occured + // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE) + try { - // 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( _ ) { - } + // Was never called and is aborted or complete + if ( callback && ( isAbort || xhr.readyState === 4 ) ) { + + // Only called once + callback = undefined; - // 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 = ""; + // Do not keep as active anymore + if ( handle ) { + xhr.onreadystatechange = jQuery.noop; + if ( xhrOnUnloadAbort ) { + delete xhrCallbacks[ handle ]; + } } - // 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 - } else if ( status === 1223 ) { - status = 204; + // 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 = 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) + try { + responses.text = xhr.responseText; + } catch( _ ) { + } + + // Firefox throws an exception when accessing + // statusText for faulty cross-domain requests + try { + statusText = xhr.statusText; + } catch( 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 && s.isLocal && !s.crossDomain ) { + status = responses.text ? 200 : 404; + // IE - #1450: sometimes returns 1223 when it should be 204 + } else if ( status === 1223 ) { + status = 204; + } } } + } catch( firefoxAccessException ) { + if ( !isAbort ) { + complete( -1, firefoxAccessException ); + } } - } catch( firefoxAccessException ) { - if (!isAbort ) { - // 调用 complete 并传递错误信息 - complete( -1, firefoxAccessException ); + + // Call complete if needed + 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 + 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 + if ( !xhrCallbacks ) { + xhrCallbacks = {}; + jQuery( window ).unload( xhrOnUnloadAbort ); + } + // Add to list of active xhrs callbacks + xhrCallbacks[ handle ] = callback; + } + xhr.onreadystatechange = callback; } + }, - // Call complete if needed - // 如果需要,调用 complete 函数 - if ( responses ) { - complete( status, statusText, responses, responseHeaders ); + abort: function() { + if ( callback ) { + callback(0,1); } - }; + } + }; + } + }); + } + + + + + var elemdisplay = {}, + iframe, iframeDoc, + rfxtypes = /^(?:toggle|show|hide)$/, + rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i, + timerId, + fxAttrs = [ + // height animations + [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ], + // width animations + [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ], + // opacity animations + [ "opacity" ] + ], + fxNow; + + jQuery.fn.extend({ + show: function( speed, easing, callback ) { + var elem, display; + + if ( speed || speed === 0 ) { + 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 + 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 + 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; + + if ( display === "" || display === "none" ) { + elem.style.display = jQuery._data( elem, "olddisplay" ) || ""; + } + } + } + + return this; + } + }, + + hide: function( speed, easing, callback ) { + if ( speed || speed === 0 ) { + return this.animate( genFx("hide", 3), speed, easing, callback); + + } else { + var elem, display, + i = 0, + j = this.length; + + for ( ; i < j; i++ ) { + elem = this[i]; + if ( elem.style ) { + display = jQuery.css( elem, "display" ); + + 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 + for ( i = 0; i < j; i++ ) { + if ( this[i].style ) { + this[i].style.display = "none"; + } + } + + return this; + } + }, + + // Save the old toggle function + _toggle: jQuery.fn.toggle, + + toggle: function( fn, fn2, callback ) { + var bool = typeof fn === "boolean"; + + if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) { + 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 { + this.animate(genFx("toggle", 3), fn, fn2, callback); + } + + return this; + }, + + fadeTo: function( speed, to, easing, callback ) { + return this.filter(":hidden").css("opacity", 0).show().end() + .animate({opacity: to}, speed, easing, callback); + }, + + animate: function( prop, speed, easing, callback ) { + var optall = jQuery.speed( speed, easing, callback ); + + if ( jQuery.isEmptyObject( prop ) ) { + return this.each( optall.complete, [ false ] ); + } - // 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(); + // Do not change referenced properties as per-property easing will be lost + prop = jQuery.extend( {}, prop ); + + function doAnimation() { + // XXX 'this' does not always have a nodeName when running the + // test suite + + if ( optall.queue === false ) { + jQuery._mark( this ); + } + + var opt = jQuery.extend( {}, optall ), + isElement = this.nodeType === 1, + hidden = isElement && jQuery(this).is(":hidden"), + name, val, p, e, hooks, replace, + 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 ) { + 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" + for ( p in replace ) { + if ( ! ( p in prop ) ) { + prop[ p ] = replace[ p ]; + } + } + } + } + + for ( name in prop ) { + val = prop[ name ]; + // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default) + if ( jQuery.isArray( val ) ) { + opt.animatedProperties[ name ] = val[ 1 ]; + val = prop[ name ] = val[ 0 ]; } else { - handle = ++xhrId; - if ( xhrOnUnloadAbort ) { - // Create the active xhrs callbacks list if needed - // 如有需要,创建活动的 xhrs 回调列表 - // and attach the unload handler - // 并添加卸载处理程序 - if (!xhrCallbacks ) { - xhrCallbacks = {}; - jQuery( window ).unload( xhrOnUnloadAbort ); + opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing'; + } + + if ( val === "hide" && hidden || val === "show" && !hidden ) { + return opt.complete.call( this ); + } + + if ( isElement && ( name === "height" || name === "width" ) ) { + // Make sure that nothing sneaks out + // Record all 3 overflow attributes because IE does not + // change the overflow attribute when overflowX and + // overflowY are set to the same value + opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ]; + + // Set display property to inline-block for height/width + // animations on inline elements that are having width/height animated + if ( jQuery.css( this, "display" ) === "inline" && + jQuery.css( this, "float" ) === "none" ) { + + // inline-level elements accept inline-block; + // block-level elements need to be inline with layout + if ( !jQuery.support.inlineBlockNeedsLayout || defaultDisplay( this.nodeName ) === "inline" ) { + this.style.display = "inline-block"; + + } else { + this.style.zoom = 1; } - // Add to list of active xhrs callbacks - // 将回调添加到活动的 xhrs 回调列表 - xhrCallbacks[ handle ] = callback; } - xhr.onreadystatechange = callback; } - }, + } - abort: function() { - if ( callback ) { - // 调用回调函数进行中止操作 - callback(0,1); + if ( opt.overflow != null ) { + this.style.overflow = "hidden"; + } + + for ( p in prop ) { + e = new jQuery.fx( this, opt, p ); + val = prop[ p ]; + + if ( rfxtypes.test( val ) ) { + + // Tracks whether to show or hide based on private + // data attached to the element + method = jQuery._data( this, "toggle" + p ) || ( val === "toggle" ? hidden ? "show" : "hide" : 0 ); + if ( method ) { + jQuery._data( this, "toggle" + p, method === "show" ? "hide" : "show" ); + e[ method ](); + } else { + e[ val ](); + } + + } else { + parts = rfxnum.exec( val ); + start = e.cur(); + + if ( parts ) { + end = parseFloat( parts[2] ); + unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" ); + + // We need to compute starting value + if ( unit !== "px" ) { + jQuery.style( this, p, (end || 1) + unit); + start = ( (end || 1) / e.cur() ) * start; + jQuery.style( this, p, start + unit); + } + + // If a +=/-= token was provided, we're doing a relative animation + if ( parts[1] ) { + end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start; + } + + e.custom( start, end, unit ); + + } else { + e.custom( start, val, "" ); + } } } + + // For JS strict compliance + return true; + } + + return optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + + stop: function( type, clearQueue, gotoEnd ) { + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue && type !== false ) { + this.queue( type || "fx", [] ); + } + + return this.each(function() { + var index, + hadTimers = false, + timers = jQuery.timers, + data = jQuery._data( this ); + + // clear marker counters if we know they won't be + if ( !gotoEnd ) { + jQuery._unmark( true, this ); + } + + function stopQueue( elem, data, index ) { + var hooks = data[ index ]; + jQuery.removeData( elem, index, true ); + hooks.stop( gotoEnd ); + } + + if ( type == null ) { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && index.indexOf(".run") === index.length - 4 ) { + stopQueue( this, data, index ); + } + } + } else if ( data[ index = type + ".run" ] && data[ index ].stop ){ + stopQueue( this, data, index ); + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) { + if ( gotoEnd ) { + + // force the next step to be the last + timers[ index ]( true ); + } else { + timers[ index ].saveState(); + } + hadTimers = true; + timers.splice( index, 1 ); + } + } + + // start the next in the queue if the last step wasn't forced + // timers currently will call their complete callbacks, which will dequeue + // but only if they were gotoEnd + if ( !( gotoEnd && hadTimers ) ) { + jQuery.dequeue( this, type ); + } + }); + } + + }); + +// Animations created synchronously will run synchronously + function createFxNow() { + setTimeout( clearFxNow, 0 ); + return ( fxNow = jQuery.now() ); + } + + function clearFxNow() { + fxNow = undefined; + } + +// Generate parameters to create a standard animation + function genFx( type, num ) { + var obj = {}; + + jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice( 0, num )), function() { + obj[ this ] = type; + }); + + return obj; + } + +// Generate shortcuts for custom animations + jQuery.each({ + slideDown: genFx( "show", 1 ), + slideUp: genFx( "hide", 1 ), + slideToggle: genFx( "toggle", 1 ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } + }, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; + }); + + jQuery.extend({ + speed: function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + jQuery.isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing }; + + opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : + opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default; + + // normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function( noUnmark ) { + if ( jQuery.isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } else if ( noUnmark !== false ) { + jQuery._unmark( this ); + } + }; + + return opt; + }, + + easing: { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return ( -Math.cos( p*Math.PI ) / 2 ) + 0.5; + } + }, + + timers: [], + + fx: function( elem, options, prop ) { + this.options = options; + this.elem = elem; + this.prop = prop; + + options.orig = options.orig || {}; } + }); -} + jQuery.fx.prototype = { + // Simple function for setting a style value + update: function() { + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + ( jQuery.fx.step[ this.prop ] || jQuery.fx.step._default )( this ); + }, + + // Get the current size + cur: function() { + if ( this.elem[ this.prop ] != null && (!this.elem.style || this.elem.style[ this.prop ] == null) ) { + return this.elem[ this.prop ]; + } + + var parsed, + r = jQuery.css( this.elem, this.prop ); + // Empty strings, null, undefined and "auto" are converted to 0, + // complex values such as "rotate(1rad)" are returned as is, + // simple values such as "10px" are parsed to Float. + return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r : parsed; + }, + // Start an animation from one number to another + custom: function( from, to, unit ) { + var self = this, + fx = jQuery.fx; + this.startTime = fxNow || createFxNow(); + this.end = to; + this.now = this.start = from; + this.pos = this.state = 0; + this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" ); -// 以下代码主要涉及 jQuery 的 AJAX 请求处理,根据不同的浏览器环境和请求情况进行相应的设置和操作 + function t( gotoEnd ) { + return self.step( gotoEnd ); + } -// 根据是否为 IE 浏览器定义 xhrOnUnloadAbort 函数,如果是 IE 浏览器,该函数会遍历 xhrCallbacks 对象,调用其中存储的回调函数并传入中止参数,否则为 false -var // #5280: Internet Explorer will keep connections alive if we don't abort on unload - xhrOnUnloadAbort = window.ActiveXObject? function() { - // 开始遍历 xhrCallbacks 对象 - for ( var key in xhrCallbacks ) { - // 调用存储在 xhrCallbacks 中的回调函数,并传入中止信号 (0, 1),表示中止请求 - xhrCallbacks[ key ]( 0, 1 ); - } - } : false, - // 初始化 xhrId 为 0,用于为请求生成唯一标识 - xhrId = 0, - // 用于存储 xhr 请求的回调函数的对象 - xhrCallbacks; + t.queue = this.options.queue; + t.elem = this.elem; + t.saveState = function() { + if ( jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) { + if ( self.options.hide ) { + jQuery._data( self.elem, "fxshow" + self.prop, self.start ); + } else if ( self.options.show ) { + jQuery._data( self.elem, "fxshow" + self.prop, self.end ); + } + } + }; + if ( t() && jQuery.timers.push(t) && !timerId ) { + timerId = setInterval( fx.tick, fx.interval ); + } + }, -// 定义 createStandardXHR 函数,尝试创建标准的 XMLHttpRequest 对象 -function createStandardXHR() { - try { - // 创建一个新的 XMLHttpRequest 对象 - return new window.XMLHttpRequest(); - } catch( e ) { - // 如果创建失败,不执行任何操作 - } -} + // Simple 'show' function + show: function() { + var dataShow = jQuery._data( this.elem, "fxshow" + this.prop ); + // Remember where we started, so that we can go back to it later + this.options.orig[ this.prop ] = dataShow || jQuery.style( this.elem, this.prop ); + this.options.show = true; -// 定义 createActiveXHR 函数,尝试创建 ActiveXObject 类型的 XMLHttpRequest 对象,主要用于兼容旧版 IE 浏览器 -function createActiveXHR() { - try { - // 创建一个 Microsoft.XMLHTTP 类型的 ActiveXObject 对象,作为 XMLHttpRequest 对象的替代 - return new window.ActiveXObject( "Microsoft.XMLHTTP" ); - } catch( e ) { - // 如果创建失败,不执行任何操作 - } -} - - -// 创建请求对象,根据浏览器是否支持 ActiveXObject 进行不同的创建方式,这里是为了向后兼容 jQuery 的 ajaxSettings -jQuery.ajaxSettings.xhr = window.ActiveXObject? - /* Microsoft 在 IE7 中未能正确实现 XMLHttpRequest(无法请求本地文件),所以当 ActiveXObject 可用时使用它 - * 另外,在 IE7/IE8 中 XMLHttpRequest 可能被禁用,所以需要一个后备方案 - */ - function() { - // 对于 IE 浏览器,如果不是本地请求,则尝试创建标准的 XMLHttpRequest 对象,若失败则创建 ActiveXObject 类型的 XMLHttpRequest 对象 - return!this.isLocal && createStandardXHR() || createActiveXHR(); - } : - // 对于其他浏览器,直接使用标准的 XMLHttpRequest 对象 - createStandardXHR; - - -// 确定浏览器的支持属性,通过立即执行函数,传入 jQuery.ajaxSettings.xhr() 的结果作为参数 -(function( xhr ) { - // 扩展 jQuery.support 对象,添加 ajax 和 cors 属性,以检查浏览器对 ajax 和跨域请求的支持情况 - jQuery.extend( jQuery.support, { - // 检查是否支持 ajax,将 xhr 对象转换为布尔值 - ajax:!!xhr, - // 检查是否支持 CORS,需要 xhr 存在且具有 withCredentials 属性 - cors:!!xhr && ( "withCredentials" in xhr ) - }); -})( jQuery.ajaxSettings.xhr() ); + // Begin the animation + // Make sure that we start at a small width/height to avoid any flash of content + if ( dataShow !== undefined ) { + // This show is picking up where a previous hide or show left off + this.custom( this.cur(), dataShow ); + } else { + this.custom( this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur() ); + } + // Start by showing the element + jQuery( this.elem ).show(); + }, -// 如果浏览器支持 ajax,创建传输对象 -if ( jQuery.support.ajax ) { - // 为 jQuery 注册一个 ajax 传输对象 - jQuery.ajaxTransport(function( s ) { - // 仅当请求不是跨域或支持跨域请求(通过 XMLHttpRequest)时才继续 - if (!s.crossDomain || jQuery.support.cors ) { - // 存储回调函数的变量 - var callback; + // Simple 'hide' function + hide: function() { + // Remember where we started, so that we can go back to it later + this.options.orig[ this.prop ] = jQuery._data( this.elem, "fxshow" + this.prop ) || jQuery.style( this.elem, this.prop ); + this.options.hide = true; - return { - send: function( headers, complete ) { - // 获取一个新的 xhr 对象 - var xhr = s.xhr(), - // 用于存储请求的处理句柄 - handle, - // 用于遍历的变量 - i; - - - // 打开 xhr 请求 - // 如果提供了用户名,使用用户名和密码打开请求,否则只使用 url 和异步标志打开请求 - // 在 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 ); - } + // Begin the animation + this.custom( this.cur(), 0 ); + }, + // Each step of an animation + step: function( gotoEnd ) { + var p, n, complete, + t = fxNow || createFxNow(), + done = true, + elem = this.elem, + options = this.options; - // 如果存在自定义的 xhr 字段,将它们应用到 xhr 对象上 - if ( s.xhrFields ) { - for ( i in s.xhrFields ) { - xhr[ i ] = s.xhrFields[ i ]; - } - } + if ( gotoEnd || t >= options.duration + this.startTime ) { + this.now = this.end; + this.pos = this.state = 1; + this.update(); + options.animatedProperties[ this.prop ] = true; - // 如果需要,使用 xhr.overrideMimeType 方法覆盖 MIME 类型 - if ( s.mimeType && xhr.overrideMimeType ) { - xhr.overrideMimeType( s.mimeType ); + for ( p in options.animatedProperties ) { + if ( options.animatedProperties[ p ] !== true ) { + done = false; } + } + if ( done ) { + // Reset the overflow + if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) { - // X-Requested-With 头处理 - // 对于跨域请求,由于预检条件复杂,不设置该请求头,对于同域请求,如果该请求头未提供,则设置为 XMLHttpRequest - // 也可以在每次请求时设置或使用 ajaxSetup 设置 - if (!s.crossDomain &&!headers["X-Requested-With"] ) { - headers[ "X-Requested-With" ] = "XMLHttpRequest"; + jQuery.each( [ "", "X", "Y" ], function( index, value ) { + elem.style[ "overflow" + value ] = options.overflow[ index ]; + }); } + // Hide the element if the "hide" operation was done + if ( options.hide ) { + jQuery( elem ).hide(); + } - // Firefox 3 中的跨域请求需要额外的 try/catch 处理 - try { - // 遍历 headers 并设置请求头 - for ( i in headers ) { - xhr.setRequestHeader( i, headers[ i ] ); + // Reset the properties, if the item has been hidden or shown + if ( options.hide || options.show ) { + for ( p in options.animatedProperties ) { + jQuery.style( elem, p, options.orig[ p ] ); + jQuery.removeData( elem, "fxshow" + p, true ); + // Toggle data is no longer needed + jQuery.removeData( elem, "toggle" + p, true ); } - } catch( _ ) { - // 异常时不做特殊处理 } + // Execute the complete function + // in the event that the complete function throws an exception + // we must ensure it won't be called twice. #5684 - // 发送请求,根据是否有内容和数据决定发送的数据,没有数据时发送 null - xhr.send( ( s.hasContent && s.data ) || null ); - + complete = options.complete; + if ( complete ) { - // 定义请求的回调函数 - callback = function( _, isAbort ) { - var status, - statusText, - responseHeaders, - responses, - xml; + options.complete = false; + complete.call( elem ); + } + } + return false; - // Firefox 在发生网络错误时访问 xhr 属性会抛出异常,参考 http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE) - try { - // 如果回调函数存在且请求已中止或完成(readyState 为 4) - if ( callback && ( isAbort || xhr.readyState === 4 ) ) { - // 确保回调函数只被调用一次 - callback = undefined; + } else { + // classical easing cannot be used with an Infinity duration + if ( options.duration == Infinity ) { + this.now = t; + } else { + n = t - this.startTime; + this.state = n / options.duration; + // Perform the easing function, defaults to swing + this.pos = jQuery.easing[ options.animatedProperties[this.prop] ]( this.state, n, 0, 1, options.duration ); + this.now = this.start + ( (this.end - this.start) * this.pos ); + } + // Perform the next step of the animation + this.update(); + } - // 如果存在句柄,移除 onreadystatechange 事件监听器,并在需要时从 xhrCallbacks 中删除该句柄 - if ( handle ) { - xhr.onreadystatechange = jQuery.noop; - if ( xhrOnUnloadAbort ) { - delete xhrCallbacks[ handle ]; - } - } + return true; + } + }; + jQuery.extend( jQuery.fx, { + tick: function() { + var timer, + timers = jQuery.timers, + i = 0; - // 如果是中止操作 - if ( isAbort ) { - // 如果请求尚未完成(readyState 不为 4),手动中止请求 - if ( xhr.readyState!== 4 ) { - xhr.abort(); - } - } else { - // 获取请求的状态码 - status = xhr.status; - // 获取响应的所有头信息 - responseHeaders = xhr.getAllResponseHeaders(); - // 存储响应信息的对象 - responses = {}; - // 获取响应的 XML 数据 - xml = xhr.responseXML; - - - // 如果响应的 XML 数据存在且有 documentElement,将其存储在 responses.xml 中(#4958) - if ( xml && xml.documentElement ) { - responses.xml = xml; - } + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + // Checks the timer has not already been removed + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + if ( !timers.length ) { + jQuery.fx.stop(); + } + }, - // 在请求二进制数据时,IE6-9 访问 responseText 会抛出异常(#11426) - try { - responses.text = xhr.responseText; - } catch( _ ) { - } + interval: 13, + stop: function() { + clearInterval( timerId ); + timerId = null; + }, - // Firefox 在访问有问题的跨域请求的 statusText 时会抛出异常 - try { - statusText = xhr.statusText; - } catch( e ) { - // 当出现异常时,使用 Webkit 规范将 statusText 设为空 - statusText = ""; - } + speeds: { + slow: 600, + fast: 200, + // Default speed + _default: 400 + }, + step: { + opacity: function( fx ) { + jQuery.style( fx.elem, "opacity", fx.now ); + }, - // 过滤非标准行为的状态 + _default: function( fx ) { + if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) { + fx.elem.style[ fx.prop ] = fx.now + fx.unit; + } else { + fx.elem[ fx.prop ] = fx.now; + } + } + } + }); +// Ensure props that can't be negative don't go there on undershoot easing + jQuery.each( fxAttrs.concat.apply( [], fxAttrs ), function( i, prop ) { + // exclude marginTop, marginLeft, marginBottom and marginRight from this list + if ( prop.indexOf( "margin" ) ) { + jQuery.fx.step[ prop ] = function( fx ) { + jQuery.style( fx.elem, prop, Math.max(0, fx.now) + fx.unit ); + }; + } + }); - // 如果请求是本地的且有数据,假设请求成功;无数据的成功不会被通知,这是根据当前实现的最佳处理方式 - if (!status && s.isLocal &&!s.crossDomain ) { - status = responses.text? 200 : 404; - // IE - #1450: 有时会返回 1223 而不是 204 - } else if ( status === 1223 ) { - status = 204; - } - } - } - } catch( firefoxAccessException ) { - // 如果不是中止操作,调用 complete 函数并传递错误信息 - if (!isAbort ) { - complete( -1, firefoxAccessException ); - } - } + if ( jQuery.expr && jQuery.expr.filters ) { + jQuery.expr.filters.animated = function( elem ) { + return jQuery.grep(jQuery.timers, function( fn ) { + return elem === fn.elem; + }).length; + }; + } +// Try to restore the default display value of an element + function defaultDisplay( nodeName ) { - // 如果有响应信息,调用 complete 函数传递状态、状态文本、响应和响应头信息 - if ( responses ) { - complete( status, statusText, responses, responseHeaders ); - } - }; + if ( !elemdisplay[ nodeName ] ) { + var body = document.body, + elem = jQuery( "<" + nodeName + ">" ).appendTo( body ), + display = elem.css( "display" ); + elem.remove(); - // 如果是同步模式或请求已在缓存中且直接获取(IE6 & IE7),手动触发回调 - if (!s.async || xhr.readyState === 4 ) { - callback(); - } else { - // 生成新的请求句柄 - handle = ++xhrId; - if ( xhrOnUnloadAbort ) { - // 如果 xhrCallbacks 不存在,创建该对象,并为 window 绑定 unload 事件,在卸载时调用 xhrOnUnloadAbort 函数 - if (!xhrCallbacks ) { - xhrCallbacks = {}; - jQuery( window ).unload( xhrOnUnloadAbort ); - } - // 将回调添加到 xhrCallbacks 中 - xhrCallbacks[ handle ] = callback; - } - // 为 xhr 对象添加 onreadystatechange 事件监听器 - xhr.onreadystatechange = callback; - } - }, + // If the simple way fails, + // get element's real default display by attaching it to a temp iframe + if ( display === "none" || display === "" ) { + // No iframe to use yet, so create it + if ( !iframe ) { + iframe = document.createElement( "iframe" ); + iframe.frameBorder = iframe.width = iframe.height = 0; + } + body.appendChild( iframe ); - abort: function() { - // 调用回调函数进行中止操作 - if ( callback ) { - callback(0,1); - } + // Create a cacheable copy of the iframe document on first call. + // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML + // document to it; WebKit & Firefox won't allow reusing the iframe document. + if ( !iframeDoc || !iframe.createElement ) { + iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document; + iframeDoc.write( ( jQuery.support.boxModel ? "<!doctype html>" : "" ) + "<html><body>" ); + iframeDoc.close(); } - }; - } - }); -} - -if ( opt.overflow!= null ) { - // 如果 opt 的 overflow 属性不为空,将元素的 overflow 样式设置为 hidden - this.style.overflow = "hidden"; -} - -for ( p in prop ) { - // 遍历 prop 对象的属性 - e = new jQuery.fx( this, opt, p ); - val = prop[ p ]; - - if ( rfxtypes.test( val ) ) { - // 检查 val 是否符合某种效果类型的测试 - // Tracks whether to show or hide based on private - // data attached to the element - // 根据元素上的私有数据来确定是显示还是隐藏 - method = jQuery._data( this, "toggle" + p ) || ( val === "toggle"? hidden? "show" : "hide" : 0 ); - if ( method ) { - // 如果 method 存在,将 toggle 属性的数据更新为相反的操作 - jQuery._data( this, "toggle" + p, method === "show"? "hide" : "show" ); - // 调用相应的方法(show 或 hide) - e[ method ](); - } else { - // 调用元素的 val 方法 - e[ val ](); - } - } else { - parts = rfxnum.exec( val ); - start = e.cur(); - if ( parts ) { - // 解析 val 得到结束值 - end = parseFloat( parts[2] ); - unit = parts[3] || ( jQuery.cssNumber[ p ]? "" : "px" ); + elem = iframeDoc.createElement( nodeName ); - // We need to compute starting value - // 我们需要计算起始值 - if ( unit!== "px" ) { - // 如果单位不是 px,将元素的样式设置为结束值加单位 - jQuery.style( this, p, (end || 1) + unit); - // 计算起始值 - start = ( (end || 1) / e.cur() ) * start; - // 将元素的样式设置为起始值加单位 - jQuery.style( this, p, start + unit); - } + iframeDoc.body.appendChild( elem ); - // If a +=/-= token was provided, we're doing a relative animation - // 如果提供了 += 或 -= 符号,进行相对动画 - if ( parts[1] ) { - end = ( (parts[ 1 ] === "-="? -1 : 1) * end ) + start; + display = jQuery.css( elem, "display" ); + body.removeChild( iframe ); } - // 执行自定义动画 - e.custom( start, end, unit ); - } else { - // 执行自定义动画,使用起始值和 val 作为结束值 - e.custom( start, val, "" ); + // Store the correct default display + elemdisplay[ nodeName ] = display; } - } -} - -// For JS strict compliance -// 为了符合 JavaScript 严格模式 -return true; -} - -return optall.queue === false? - // 如果队列选项为 false,对每个元素执行 doAnimation 函数 - this.each( doAnimation ) : - // 否则将 doAnimation 函数添加到队列中 - this.queue( optall.queue, doAnimation ); -}, - -stop: function( type, clearQueue, gotoEnd ) { - if ( typeof type!== "string" ) { - // 调整参数,如果 type 不是字符串 - gotoEnd = clearQueue; - clearQueue = type; - type = undefined; - } - if ( clearQueue && type!== false ) { - // 如果 clearQueue 为真且 type 不为 false,将相应队列清空 - this.queue( type || "fx", [] ); + + return elemdisplay[ nodeName ]; } - return this.each(function() { - var index, - hadTimers = false, - timers = jQuery.timers, - data = jQuery._data( this ); - // clear marker counters if we know they won't be - // 如果不跳转到结尾,清除标记 - if (!gotoEnd ) { - jQuery._unmark( true, this ); - } - function stopQueue( elem, data, index ) { - // 从元素中移除相应数据并停止动画 - jQuery.removeData( elem, index, true ); - data[ index ].stop( gotoEnd ); - } - if ( type == null ) { - for ( index in data ) { - if ( data[ index ] && data[ index ].stop && index.indexOf(".run") === index.length - 4 ) { - stopQueue( this, data, index ); - } - } - } else if ( data[ index = type + ".run" ] && data[ index ].stop ){ - stopQueue( this, data, index ); - } + var getOffset, + rtable = /^t(?:able|d|h)$/i, + rroot = /^(?:body|html)$/i; - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) { - if ( gotoEnd ) { - // force the next step to be the last - // 强制下一个步骤为最后一个 - timers[ index ]( true ); - } else { - // 保存状态 - timers[ index ].saveState(); - } - hadTimers = true; - // 从定时器数组中移除 - timers.splice( index, 1 ); - } - } + if ( "getBoundingClientRect" in document.documentElement ) { + getOffset = function( elem, doc, docElem, box ) { + try { + box = elem.getBoundingClientRect(); + } catch(e) {} - // start the next in the queue if the last step wasn't forced - // 如果最后一步不是强制的,开始队列中的下一个元素 - // timers currently will call their complete callbacks, which will dequeue - // but only if they were gotoEnd - if (!( gotoEnd && hadTimers ) ) { - jQuery.dequeue( this, type ); - } - }); -} + // Make sure we're not dealing with a disconnected DOM node + if ( !box || !jQuery.contains( docElem, elem ) ) { + return box ? { top: box.top, left: box.left } : { top: 0, left: 0 }; + } -}); + var body = doc.body, + win = getWindow( doc ), + clientTop = docElem.clientTop || body.clientTop || 0, + clientLeft = docElem.clientLeft || body.clientLeft || 0, + scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop, + scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft, + top = box.top + scrollTop - clientTop, + left = box.left + scrollLeft - clientLeft; -// Animations created synchronously will run synchronously -// 同步创建的动画将同步运行 -function createFxNow() { - // 设置一个延迟为 0 的定时器,用于清除 fxNow - setTimeout( clearFxNow, 0 ); - return ( fxNow = jQuery.now() ); -} - -function clearFxNow() { - // 清除 fxNow - fxNow = undefined; -} + return { top: top, left: left }; + }; -// Generate parameters to create a standard animation -// 生成创建标准动画的参数 -function genFx( type, num ) { - var obj = {}; + } else { + getOffset = function( elem, doc, docElem ) { + var computedStyle, + offsetParent = elem.offsetParent, + prevOffsetParent = elem, + body = doc.body, + defaultView = doc.defaultView, + prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle, + top = elem.offsetTop, + left = elem.offsetLeft; + + while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) { + if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) { + break; + } - jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice( 0, num )), function() { - // 为生成的对象添加相应属性 - obj[ this ] = type; - }); + computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle; + top -= elem.scrollTop; + left -= elem.scrollLeft; - return obj; -} + if ( elem === offsetParent ) { + top += elem.offsetTop; + left += elem.offsetLeft; -// Generate shortcuts for custom animations -// 为自定义动画生成快捷方式 -jQuery.each({ - slideDown: genFx( "show", 1 ), - slideUp: genFx( "hide", 1 ), - slideToggle: genFx( "toggle", 1 ), - fadeIn: { opacity: "show" }, - fadeOut: { opacity: "hide" }, - fadeToggle: { opacity: "toggle" } -}, function( name, props ) { - jQuery.fn[ name ] = function( speed, easing, callback ) { - // 为 jQuery 元素添加相应的动画方法 - return this.animate( props, speed, easing, callback ); - }; -}); - -jQuery.extend({ - speed: function( speed, easing, fn ) { - var opt = speed && typeof speed === "object"? jQuery.extend( {}, speed ) : { - // 解析速度选项 - complete: fn ||!fn && easing || - jQuery.isFunction( speed ) && speed, - duration: speed, - easing: fn && easing || easing &&!jQuery.isFunction( easing ) && easing - }; + if ( jQuery.support.doesNotAddBorder && !(jQuery.support.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) { + top += parseFloat( computedStyle.borderTopWidth ) || 0; + left += parseFloat( computedStyle.borderLeftWidth ) || 0; + } - opt.duration = jQuery.fx.off? 0 : typeof opt.duration === "number"? opt.duration : - // 获取持续时间 - opt.duration in jQuery.fx.speeds? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default; + prevOffsetParent = offsetParent; + offsetParent = elem.offsetParent; + } - // normalize opt.queue - true/undefined/null -> "fx" - // 规范化队列选项 - if ( opt.queue == null || opt.queue === true ) { - opt.queue = "fx"; - } + if ( jQuery.support.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) { + top += parseFloat( computedStyle.borderTopWidth ) || 0; + left += parseFloat( computedStyle.borderLeftWidth ) || 0; + } - // Queueing - // 处理队列 - opt.old = opt.complete; + prevComputedStyle = computedStyle; + } - opt.complete = function( noUnmark ) { - if ( jQuery.isFunction( opt.old ) ) { - // 调用旧的完成函数 - opt.old.call( this ); + if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) { + top += body.offsetTop; + left += body.offsetLeft; } - if ( opt.queue ) { - // 从队列中移除元素 - jQuery.dequeue( this, opt.queue ); - } else if ( noUnmark!== false ) { - // 取消标记 - jQuery._unmark( this ); + if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) { + top += Math.max( docElem.scrollTop, body.scrollTop ); + left += Math.max( docElem.scrollLeft, body.scrollLeft ); } - }; - return opt; - }, + return { top: top, left: left }; + }; + } - easing: { - // 线性缓动函数 - linear: function( p ) { - return p; - }, - // 摆动缓动函数 - swing: function( p ) { - return ( -Math.cos( p*Math.PI ) / 2 ) + 0.5; + jQuery.fn.offset = function( options ) { + if ( arguments.length ) { + return options === undefined ? + this : + this.each(function( i ) { + jQuery.offset.setOffset( this, options, i ); + }); } - }, - - timers: [], - fx: function( elem, options, prop ) { - this.options = options; - this.elem = elem; - this.prop = prop; - - options.orig = options.orig || {}; - } + var elem = this[0], + doc = elem && elem.ownerDocument; -}); + if ( !doc ) { + return null; + } -jQuery.fx.prototype = { - // Simple function for setting a style value - // 用于设置样式值的简单函数 - update: function() { - if ( this.options.step ) { - // 调用步骤函数 - this.options.step.call( this.elem, this.now, this ); + if ( elem === doc.body ) { + return jQuery.offset.bodyOffset( elem ); } - // 调用相应的步骤函数或默认步骤函数 - ( jQuery.fx.step[ this.prop ] || jQuery.fx.step._default )( this ); - }, + return getOffset( elem, doc, doc.documentElement ); + }; - // Get the current size - // 获取当前尺寸 - cur: function() { - if ( this.elem[ this.prop ]!= null && (!this.elem.style || this.elem.style[ this.prop ] == null) ) { - return this.elem[ this.prop ]; - } + jQuery.offset = { - var parsed, - r = jQuery.css( this.elem, this.prop ); - // Empty strings, null, undefined and "auto" are converted to 0, - // complex values such as "rotate(1rad)" are returned as is, - // simple values such as "10px" are parsed to Float. - // 空字符串、null、undefined 和 "auto" 转换为 0,复杂值如 "rotate(1rad)" 原样返回,简单值如 "10px" 解析为浮点数 - return isNaN( parsed = parseFloat( r ) )?!r || r === "auto"? 0 : r : parsed; - }, - - // Start an animation from one number to another - // 从一个数字开始动画到另一个数字 - custom: function( from, to, unit ) { - var self = this, - fx = jQuery.fx; - - this.startTime = fxNow || createFxNow(); - this.end = to; - this.now = this.start = from; - this.pos = this.state = 0; - this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ]? "" : "px" ); - - function t( gotoEnd ) { - return self.step( gotoEnd ); - } + bodyOffset: function( body ) { + var top = body.offsetTop, + left = body.offsetLeft; - t.queue = this.options.queue; - t.elem = this.elem; - t.saveState = function() { - if ( jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) { - if ( self.options.hide ) { - jQuery._data( self.elem, "fxshow" + self.prop, self.start ); - } else if ( self.options.show ) { - jQuery._data( self.elem, "fxshow" + self.prop, self.end ); - } + if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) { + top += parseFloat( jQuery.css(body, "marginTop") ) || 0; + left += parseFloat( jQuery.css(body, "marginLeft") ) || 0; } - }; - if ( t() && jQuery.timers.push(t) &&!timerId ) { - timerId = setInterval( fx.tick, fx.interval ); - } - }, - - // Simple 'show' function - // 简单的显示函数 - show: function() { - var dataShow = jQuery._data( this.elem, "fxshow" + this.prop ); - - // Remember where we started, so that we can go back to it later - // 记住开始的位置,以便之后返回 - this.options.orig[ this.prop ] = dataShow || jQuery.style( this.elem, this.prop ); - this.options.show = true; - - // Begin the animation - // 开始动画 - // Make sure that we start at a small width/height to avoid any flash of content - // 确保从较小的宽度/高度开始,以避免内容闪烁 - if ( dataShow!== undefined ) { - // This show is picking up where a previous hide or show left off - // 这个显示操作从上一次隐藏或显示的位置继续 - this.custom( this.cur(), dataShow ); - } else { - this.custom( this.prop === "width" || this.prop === "height"? 1 : 0, this.cur() ); - } + return { top: top, left: left }; + }, - // Start by showing the element - // 首先显示元素 - jQuery( this.elem ).show(); - }, - - // Simple 'hide' function - // 简单的隐藏函数 - hide: function() { - // Remember where we started, so that we can go back to it. - // 记住开始的位置,以便之后返回 - this.options.orig[ this.prop ] = jQuery._data( this.elem, "fxshow" + this.prop ) || jQuery.style( this.elem, this.prop ); - this.options.hide = true; - - // Begin the animation - // 开始动画 - this.custom( this.cur(), 0 ); - }, - - // Each step of an animation - // 动画的每一步 - step: function( gotoEnd ) { - var p, n, complete, - t = fxNow || createFxNow(), - done = true, - elem = this.elem, - options = this.options; - - if ( gotoEnd || t >= options.duration + this.startTime ) { - this.now = this.end; - this.pos = this.state = 1; - this.update(); - - options.animatedProperties[ this.prop ] = true; - - for ( p in options.animatedProperties ) { - if ( options.animatedProperties[ p ]!== true ) { - done = false; - } - } - - if ( done ) { - // Reset the overflow - // 重置 overflow - if ( options.overflow!= null &&!jQuery.support.shrinkWrapBlocks ) { - - jQuery.each( [ "", "X", "Y" ], function( index, value ) { - elem.style[ "overflow" + value ] = options.overflow[ index ]; - }); - } + setOffset: function( elem, options, i ) { + var position = jQuery.css( elem, "position" ); - // Hide the element if the "hide" operation was done - // 如果是执行了隐藏操作,隐藏元素 - if ( options.hide ) { - jQuery( elem ).hide(); - } + // set position first, in-case top/left are set even on static elem + if ( position === "static" ) { + elem.style.position = "relative"; + } - // Reset the properties, if the item has been hidden or shown - // 如果元素已被隐藏或显示,重置属性 - if ( options.hide || options.show ) { - for ( p in options.animatedProperties ) { - jQuery.style( elem, p, options.orig[ p ] ); - jQuery.removeData( elem, "fxshow" + p, true ); - // Toggle data is no longer needed - jQuery.removeData( elem, "toggle" + p, true ); - } - } + var curElem = jQuery( elem ), + curOffset = curElem.offset(), + curCSSTop = jQuery.css( elem, "top" ), + curCSSLeft = jQuery.css( elem, "left" ), + calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1, + props = {}, curPosition = {}, curTop, curLeft; + + // need to be able to calculate position if either top or left is auto and position is either absolute or fixed + if ( calculatePosition ) { + curPosition = curElem.position(); + curTop = curPosition.top; + curLeft = curPosition.left; + } else { + curTop = parseFloat( curCSSTop ) || 0; + curLeft = parseFloat( curCSSLeft ) || 0; } - // Execute the complete function - // in the event that the complete function throws an exception - // we must ensure it won't be called twice. #5684 + if ( jQuery.isFunction( options ) ) { + options = options.call( elem, i, curOffset ); + } - // 将 options 中的 complete 赋值给 complete 变量 - complete = options.complete; -// 如果 complete 为真 - if ( complete ) { - // 将 options.complete 设为 false - options.complete = false; - // 调用 complete 函数,并将 elem 作为参数传递 - complete.call( elem ); + if ( options.top != null ) { + props.top = ( options.top - curOffset.top ) + curTop; } - } -// 返回 false - return false; -// 否则 - } else { - // 经典的缓动效果不能用于无限时长 - if ( options.duration == Infinity ) { - // 将当前时间 t 赋值给 this.now - this.now = t; -} else { - // 计算经过的时间 n,等于当前时间 t 减去开始时间 this.startTime - n = t - this.startTime; - // 计算动画状态,等于经过的时间 n 除以总时长 options.duration - this.state = n / options.duration; - // 执行缓动函数,默认为 swing,根据动画属性从 jQuery.easing 中选择相应的缓动函数 - this.pos = jQuery.easing[ options.animatedProperties[this.prop] ]( this.state, n, 0, 1, options.duration ); - // 计算当前位置,根据起始位置、结束位置和缓动后的位置比例计算 - this.now = this.start + ( (this.end - this.start) * this.pos ); -} -// 执行动画的下一步 -this.update(); -} -// 返回 true -return true; -} -}; -// 扩展 jQuery.fx 对象 -jQuery.extend( jQuery.fx, { - // 定义 tick 函数 - tick: function() { - // 定义变量 timer 和 timers,将 jQuery.timers 赋值给 timers - var timer, - timers = jQuery.timers, - i = 0; - // 遍历 timers 数组 - for ( ; i < timers.length; i++ ) { - // 将当前元素赋值给 timer - timer = timers[ i ]; - // 检查定时器是否未被移除,如果是则将其从数组中移除 - if (!timer() && timers[ i ] === timer ) { - timers.splice( i--, 1 ); + if ( options.left != null ) { + props.left = ( options.left - curOffset.left ) + curLeft; } - } - // 如果 timers 数组为空 - if (!timers.length ) { - // 调用 jQuery.fx.stop 函数 - jQuery.fx.stop(); - } - }, - // 定义间隔时间为 13 - interval: 13, - // 定义 stop 函数 - stop: function() { - // 清除定时器 - clearInterval( timerId ); - // 将 timerId 设为 null - timerId = null; - }, - // 定义速度对象 - speeds: { - // 慢速度为 600 - slow: 600, - // 快速度为 200 - fast: 200, - // 默认速度为 400 - // Default speed - _default: 400 - }, - // 定义 step 对象 - step: { - // 针对 opacity 属性的动画步骤函数 - opacity: function( fx ) { - // 设置元素的 opacity 样式为 fx.now - jQuery.style( fx.elem, "opacity", fx.now ); - }, - // 默认的动画步骤函数 - _default: function( fx ) { - // 如果元素有 style 属性且相应的属性不为空 - if ( fx.elem.style && fx.elem.style[ fx.prop ]!= null ) { - // 设置元素的相应属性为 fx.now 加上单位 - fx.elem.style[ fx.prop ] = fx.now + fx.unit; + + if ( "using" in options ) { + options.using.call( elem, props ); } else { - // 直接设置元素的相应属性为 fx.now - fx.elem[ fx.prop ] = fx.now; + curElem.css( props ); } } - } -}); -// 确保不能为负的属性在缓动时不会出现负值 -jQuery.each( fxAttrs.concat.apply( [], fxAttrs ), function( i, prop ) { - // 排除 margin 相关属性(除了 marginTop, marginLeft, marginBottom 和 marginRight) - if ( prop.indexOf( "margin" ) ) { - // 为相应属性定义动画步骤函数 - jQuery.fx.step[ prop ] = function( fx ) { - // 设置元素的相应属性,取最大值 0 和 fx.now 加上单位 - jQuery.style( fx.elem, prop, Math.max(0, fx.now) + fx.unit ); - }; - } -}); -// 如果 jQuery.expr 和 jQuery.expr.filters 存在 -if ( jQuery.expr && jQuery.expr.filters ) { - // 为 jQuery.expr.filters 定义 animated 函数 - jQuery.expr.filters.animated = function( elem ) { - // 使用 jQuery.grep 过滤 jQuery.timers 数组,筛选出元素等于 fn.elem 的元素 - return jQuery.grep(jQuery.timers, function( fn ) { - return elem === fn.elem; - }).length; - }; -} -// 尝试恢复元素的默认显示值 -function defaultDisplay( nodeName ) { - // 如果 elemdisplay[ nodeName ] 不存在 - if (!elemdisplay[ nodeName ] ) { - // 创建 body 元素 - var body = document.body, - // 创建一个新元素 - elem = jQuery( "<" + nodeName + ">" ).appendTo( body ), - // 获取元素的显示样式 - display = elem.css( "display" ); - // 移除元素 - elem.remove(); - // 如果显示样式为 none 或空 - if ( display === "none" || display === "" ) { - // 如果 iframe 不存在,创建 iframe - if (!iframe ) { - iframe = document.createElement( "iframe" ); - iframe.frameBorder = iframe.width = iframe.height = 0; - } - // 将 iframe 添加到 body 中 - body.appendChild( iframe ); - // 如果 iframeDoc 不存在或 iframe 不支持 createElement - if (!iframeDoc ||!iframe.createElement ) { - // 获取 iframe 的文档对象 - iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document; - // 写入文档内容 - iframeDoc.write( ( jQuery.support.boxModel? "<!doctype html>" : "" ) + "<html><body>" ); - // 关闭文档 - iframeDoc.close(); - } - // 在 iframe 文档中创建元素 - elem = iframeDoc.createElement( nodeName ); - // 将元素添加到 iframe 文档的 body 中 - iframeDoc.body.appendChild( elem ); - // 获取元素的显示样式 - display = jQuery.css( elem, "display" ); - // 从 body 中移除 iframe - body.removeChild( iframe ); - } - // 存储元素的正确默认显示值 - elemdisplay[ nodeName ] = display; - } - // 返回元素的默认显示值 - return elemdisplay[ nodeName ]; -} -// 定义变量 getOffset 和正则表达式 rtable、rroot -var getOffset, - rtable = /^t(?:able|d|h)$/i, - rroot = /^(?:body|html)$/i; -// 如果 document.documentElement 支持 getBoundingClientRect 方法 -if ( "getBoundingClientRect" in document.documentElement ) { - // 定义 getOffset 函数 - getOffset = function( elem, doc, docElem, box ) { - try { - // 获取元素的边界矩形 - box = elem.getBoundingClientRect(); - } catch(e) {} - // 确保元素在 DOM 中 - if (!box ||!jQuery.contains( docElem, elem ) ) { - // 如果获取失败或元素不在 DOM 中,返回默认的偏移量 - return box? { top: box.top, left: box.left } : { top: 0, left: 0 }; - } - // 获取 body 元素 - var body = doc.body, - // 获取窗口对象 - win = getWindow( doc ), - // 获取客户端顶部偏移量 - clientTop = docElem.clientTop || body.clientTop || 0, - // 获取客户端左侧偏移量 - clientLeft = docElem.clientLeft || body.clientLeft || 0, - // 获取页面垂直滚动距离 - scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop, - // 获取页面水平滚动距离 - scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft, - // 计算顶部偏移量 - top = box.top + scrollTop - clientTop, - // 计算左侧偏移量 - left = box.left + scrollLeft - clientLeft; - // 返回元素的偏移量 - return { top: top, left: left }; }; -// 否则 -} else { - // 另一种获取偏移量的函数 - getOffset = function( elem, doc, docElem ) { - // 定义变量 computedStyle - var computedStyle, - // 获取元素的偏移父元素 - offsetParent = elem.offsetParent, - // 上一个偏移父元素 - prevOffsetParent = elem, - // 获取 body 元素 - body = doc.body, - // 获取文档的默认视图 - defaultView = doc.defaultView, - // 获取元素的计算样式 - prevComputedStyle = defaultView? defaultView.getComputedStyle( elem, null ) : elem.currentStyle, - // 获取元素的顶部偏移量 - top = elem.offsetTop, - // 获取元素的左侧偏移量 - left = elem.offsetLeft; - // 遍历元素的父元素 - while ( (elem = elem.parentNode) && elem!== body && elem!== docElem ) { - // 如果支持固定定位且上一个元素的位置是固定的 - if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) { - break; - } - // 获取元素的计算样式 - computedStyle = defaultView? defaultView.getComputedStyle(elem, null) : elem.currentStyle; - // 减去元素的滚动距离 - top -= elem.scrollTop; - left -= elem.scrollLeft; - // 如果元素是偏移父元素 - if ( elem === offsetParent ) { - // 加上偏移父元素的偏移量 - top += elem.offsetTop; - left += elem.offsetLeft; - // 如果不添加边框且元素不是 table 或单元格 - if ( jQuery.support.doesNotAddBorder &&!(jQuery.support.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) { - // 加上边框宽度 - top += parseFloat( computedStyle.borderTopWidth ) || 0; - left += parseFloat( computedStyle.borderLeftWidth ) || 0; - } - // 更新上一个偏移父元素 - prevOffsetParent = offsetParent; - // 更新偏移父元素 - offsetParent = elem.offsetParent; - } - // 如果支持减去不可见溢出元素的边框且元素的溢出不是可见的 - if ( jQuery.support.subtractsBorderForOverflowNotVisible && computedStyle.overflow!== "visible" ) { - // 加上边框宽度 - top += parseFloat( computedStyle.borderTopWidth ) || 0; - left += parseFloat( computedStyle.borderLeftWidth ) || 0; + + + jQuery.fn.extend({ + + position: function() { + if ( !this[0] ) { + return null; } - // 更新计算样式 - prevComputedStyle = computedStyle; - } - // 如果上一个元素的位置是相对或静态的 - if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) { - // 加上 body 的偏移量 - top += body.offsetTop; - left += body.offsetLeft; - } - // 如果支持固定定位且上一个元素的位置是固定的 - if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) { - // 加上最大的滚动距离 - top += Math.max( docElem.scrollTop, body.scrollTop ); - left += Math.max( docElem.scrollLeft, body.scrollLeft ); - } - // 返回元素的偏移量 - return { top: top, left: left }; - }; -} -// 为 jQuery.fn.offset 函数添加方法 -jQuery.fn.offset = function( options ) { - // 如果有参数 - if ( arguments.length ) { - // 如果 options 为 undefined,返回 this,否则遍历元素并调用 jQuery.offset.setOffset 函数 - return options === undefined? - this : - this.each(function( i ) { - jQuery.offset.setOffset( this, options, i ); + + var elem = this[0], + + // Get *real* offsetParent + offsetParent = this.offsetParent(), + + // Get correct offsets + offset = this.offset(), + parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset(); + + // Subtract element margins + // note: when an element has margin: auto the offsetLeft and marginLeft + // are the same in Safari causing offset.left to incorrectly be 0 + offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0; + offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0; + + // Add offsetParent borders + parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0; + parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0; + + // Subtract the two offsets + return { + top: offset.top - parentOffset.top, + left: offset.left - parentOffset.left + }; + }, + + offsetParent: function() { + return this.map(function() { + var offsetParent = this.offsetParent || document.body; + while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) { + offsetParent = offsetParent.offsetParent; + } + return offsetParent; }); - } - // 获取第一个元素 - var elem = this[0], - // 获取元素的所有者文档 - doc = elem && elem.ownerDocument; - // 如果文档不存在 - if (!doc ) { - // 返回 null - return null; - } - // 如果元素是 body - if ( elem === doc.body ) { - // 获取 body 的偏移量 - return jQuery.offset.bodyOffset( elem ); - } - // 调用 getOffset 函数获取偏移量 - return getOffset( elem, doc, doc.documentElement ); -}; -// 定义 jQuery.offset 对象 -jQuery.offset = { - // 获取 body 的偏移量 - bodyOffset: function( body ) { - // 获取 body 的顶部和左侧偏移量 - var top = body.offsetTop, - left = body.offsetLeft; - // 如果不包含 body 中的 margin 到偏移量中 - if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) { - // 加上 margin 顶部和左侧的值 - top += parseFloat( jQuery.css(body, "marginTop") ) || 0; - left += parseFloat( jQuery.css(body, "marginLeft") ) || 0; - } - // 返回偏移量 - return { top: top, left: left }; - }, - // 设置元素的偏移量 - setOffset: function( elem, options, i ) { - // 获取元素的位置 - var position = jQuery.css( elem, "position" ); - // 如果元素是静态位置,将其设为相对位置 - if ( position === "static" ) { - elem.style.position = "relative"; - } - // 获取元素的 jQuery 对象 - var curElem = jQuery( elem ), - // 获取元素的偏移量 - curOffset = curElem.offset(), - // 获取元素的顶部样式 - curCSSTop = jQuery.css( elem, "top" ), - // 获取元素的左侧样式 - curCSSLeft = jQuery.css( elem, "left" ), - // 判断是否需要计算位置 - calculatePosition = ( position === "absolute" || position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1, - // 定义 props 和 curPosition 对象 - props = {}, curPosition = {}, curTop, curLeft; - // 如果需要计算位置 - if ( calculatePosition ) { - // 获取元素的位置 - curPosition = curElem.position(); - // 获取元素的顶部位置 - curTop = curPosition.top; - // 获取元素的左侧位置 - curLeft = curPosition.left; - } else { - // 获取元素的顶部和左侧位置 - curTop = parseFloat( curCSSTop ) || 0; - curLeft = parseFloat( curCSSLeft ) || 0; - } - // 如果 options 是一个函数 - if ( jQuery.isFunction( options ) ) { - // 调用函数并传递参数 - options = options.call( elem, i, curOffset ); - } - // 如果 options 中设置了顶部偏移量 - if ( options.top!= null ) { - // 计算新的顶部偏移量 - props.top = ( options.top - curOffset.top ) + curTop; - } - // 如果 options 中设置了左侧偏移量 - if ( options.left!= null ) { - // 计算新的左侧偏移量 - props.left = ( options.left - curOffset.left ) + curLeft; - } - // 如果 options 中使用了 using 函数 - if ( "using" in options ) { - // 调用 using 函数 - options.using.call( elem, props ); - } else { - // 设置元素的样式 - curElem.css( props ); - } - } -}; - -// 扩展 jQuery.fn 对象 -jQuery.fn.extend({ - // 定义 position 函数 - position: function() { - // 如果没有元素,返回 null - if (!this[0] ) { - return null; } - // 获取第一个元素 - var elem = this[0], - // 获取元素的偏移父元素 - // Get *real* offsetParent - offsetParent = this.offsetParent(), - // 获取元素的偏移量 - // Get correct offsets - offset = this.offset(), - // 如果偏移父元素是 body 或 html,设置 parentOffset 为 { top: 0, left: 0 },否则获取偏移父元素的偏移量 - parentOffset = rroot.test(offsetParent[0].nodeName)? { top: 0, left: 0 } : offsetParent.offset(); - // 减去元素的 marginTop 边距 - // Subtract element margins - // note: when an element has margin: auto the offsetLeft and marginLeft - // are the same in Safari causing offset.left to incorrectly be 0 - offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0; - // 减去元素的 marginLeft 边距 - offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0; - // 加上偏移父元素的 borderTopWidth 边框宽度 - // Add offsetParent borders - parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0; - // 加上偏移父元素的 borderLeftWidth 边框宽度 - parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0; - // 计算并返回最终的位置,为元素偏移量减去偏移父元素的偏移量 - // Subtract the two offsets - return { - top: offset.top - parentOffset.top, - left: offset.left - parentOffset.left - }; - }, - // 定义 offsetParent 函数 - offsetParent: function() { - // 对元素集合进行映射操作 - return this.map(function() { - // 获取元素的偏移父元素,如果没有则为 document.body - var offsetParent = this.offsetParent || document.body; - // 当偏移父元素存在且不是 body 或 html 且位置为 static 时,继续查找上一级偏移父元素 - while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) { - offsetParent = offsetParent.offsetParent; - } - // 返回最终的偏移父元素 - return offsetParent; - }); - } -}); + }); -// 遍历 scrollLeft 和 scrollTop 及其对应的 pageXOffset 和 pageYOffset 属性 // Create scrollLeft and scrollTop methods -jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) { - // 判断 prop 是否包含 Y - var top = /Y/.test( prop ); - // 为 jQuery.fn 扩展相应的方法 - jQuery.fn[ method ] = function( val ) { - // 调用 jQuery.access 函数 - return jQuery.access( this, function( elem, method, val ) { - // 获取元素的窗口对象 - var win = getWindow( elem ); - // 如果 val 未定义 - if ( val === undefined ) { - // 如果存在窗口对象,根据不同情况获取滚动值 - return win? (prop in win)? win[ prop ] : - jQuery.support.boxModel && win.document.documentElement[ method ] || - win.document.body[ method ] : - elem[ method ]; - } - // 如果存在窗口对象 - if ( win ) { - // 调用 scrollTo 方法设置滚动位置 - win.scrollTo( - // 根据 top 判断是设置水平还是垂直滚动位置 - !top? val : jQuery( win ).scrollLeft(), - top? val : jQuery( win ).scrollTop() - ); - } else { - // 直接设置元素的滚动属性 - elem[ method ] = val; - } - }, method, val, arguments.length, null ); - }; -}); + jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) { + var top = /Y/.test( prop ); + + jQuery.fn[ method ] = function( val ) { + return jQuery.access( this, function( elem, method, val ) { + var win = getWindow( elem ); + + if ( val === undefined ) { + return win ? (prop in win) ? win[ prop ] : + jQuery.support.boxModel && win.document.documentElement[ method ] || + win.document.body[ method ] : + elem[ method ]; + } + + if ( win ) { + win.scrollTo( + !top ? val : jQuery( win ).scrollLeft(), + top ? val : jQuery( win ).scrollTop() + ); + + } else { + elem[ method ] = val; + } + }, method, val, arguments.length, null ); + }; + }); + + function getWindow( elem ) { + return jQuery.isWindow( elem ) ? + elem : + elem.nodeType === 9 ? + elem.defaultView || elem.parentWindow : + false; + } + -// 定义 getWindow 函数,用于获取元素的窗口对象 -function getWindow( elem ) { - // 如果元素是窗口,直接返回 - return jQuery.isWindow( elem )? - elem : - // 如果元素是文档节点,返回其默认视图或父窗口 - elem.nodeType === 9? - elem.defaultView || elem.parentWindow : - false; -} -// 遍历 Height 和 Width 属性,创建一系列方法 // Create width, height, innerHeight, innerWidth, outerHeight and outerWidth methods -jQuery.each( { Height: "width", Width: "height" }, function( name, type ) { - // 生成相应的 client、scroll 和 offset 属性 - var clientProp = "client" + name, - scrollProp = "scroll" + name, - offsetProp = "offset" + name; - // 定义 innerHeight 和 innerWidth 方法 - // innerHeight and innerWidth - jQuery.fn[ "inner" + name ] = function() { - // 获取第一个元素 - var elem = this[0]; - // 如果元素存在且有样式,使用 jQuery.css 获取元素的尺寸(包含内边距),否则使用元素的相应方法 - return elem? - elem.style? - parseFloat( jQuery.css( elem, type, "padding" ) ) : - this[ type ]() : - null; - }; - // 定义 outerHeight 和 outerWidth 方法 - // outerHeight and outerWidth - jQuery.fn[ "outer" + name ] = function( margin ) { - // 获取第一个元素 - var elem = this[0]; - // 如果元素存在且有样式,使用 jQuery.css 获取元素的尺寸(根据 margin 参数包含边距或边框),否则使用元素的相应方法 - return elem? - elem.style? - parseFloat( jQuery.css( elem, type, margin? "margin" : "border" ) ) : - this[ type ]() : - null; - }; - // 定义 width 或 height 方法 - jQuery.fn[ type ] = function( value ) { - // 调用 jQuery.access 函数 - return jQuery.access( this, function( elem, type, value ) { - var doc, docElemProp, orig, ret; - // 如果元素是窗口 - if ( jQuery.isWindow( elem ) ) { - // 获取文档对象和相应的文档元素属性 - doc = elem.document; - docElemProp = doc.documentElement[ clientProp ]; - // 根据 boxModel 支持情况获取相应的尺寸 - return jQuery.support.boxModel && docElemProp || - doc.body && doc.body[ clientProp ] || docElemProp; - } - // 如果元素是文档节点 - if ( elem.nodeType === 9 ) { - // 获取文档元素 - doc = elem.documentElement; - // 如果 client 尺寸大于等于 scroll 尺寸,使用 client 尺寸,否则取最大的 scroll 或 offset 尺寸 - // when a window > document, IE6 reports a offset[Width/Height] > client[Width/Height] - // so we can't use max, as it'll choose the incorrect offset[Width/Height] - // instead we use the correct client[Width/Height] - // support:IE6 - if ( doc[ clientProp ] >= doc[ scrollProp ] ) { - return doc[ clientProp ]; - } - return Math.max( - elem.body[ scrollProp ], doc[ scrollProp ], - elem.body[ offsetProp ], doc[ offsetProp ] - ); - } - // 如果 value 未定义,使用 jQuery.css 获取元素的尺寸,并将结果转为浮点数 - if ( value === undefined ) { - orig = jQuery.css( elem, type ); - ret = parseFloat( orig ); - // 如果是数字,返回数字,否则返回原始结果 - return jQuery.isNumeric( ret )? ret : orig; - } - // 设置元素的宽度或高度 - jQuery( elem ).css( type, value ); - }, type, value, arguments.length, null ); - }; -}); + jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { + var clientProp = "client" + name, + scrollProp = "scroll" + name, + offsetProp = "offset" + name; + + // innerHeight and innerWidth + jQuery.fn[ "inner" + name ] = function() { + var elem = this[0]; + return elem ? + elem.style ? + parseFloat( jQuery.css( elem, type, "padding" ) ) : + this[ type ]() : + null; + }; + + // outerHeight and outerWidth + jQuery.fn[ "outer" + name ] = function( margin ) { + var elem = this[0]; + return elem ? + elem.style ? + parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) : + this[ type ]() : + null; + }; + + jQuery.fn[ type ] = function( value ) { + return jQuery.access( this, function( elem, type, value ) { + var doc, docElemProp, orig, ret; + + if ( jQuery.isWindow( elem ) ) { + // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat + doc = elem.document; + docElemProp = doc.documentElement[ clientProp ]; + return jQuery.support.boxModel && docElemProp || + doc.body && doc.body[ clientProp ] || docElemProp; + } + + // Get document width or height + if ( elem.nodeType === 9 ) { + // Either scroll[Width/Height] or offset[Width/Height], whichever is greater + doc = elem.documentElement; + + // when a window > document, IE6 reports a offset[Width/Height] > client[Width/Height] + // so we can't use max, as it'll choose the incorrect offset[Width/Height] + // instead we use the correct client[Width/Height] + // support:IE6 + if ( doc[ clientProp ] >= doc[ scrollProp ] ) { + return doc[ clientProp ]; + } + + return Math.max( + elem.body[ scrollProp ], doc[ scrollProp ], + elem.body[ offsetProp ], doc[ offsetProp ] + ); + } + + // Get width or height on the element + if ( value === undefined ) { + orig = jQuery.css( elem, type ); + ret = parseFloat( orig ); + return jQuery.isNumeric( ret ) ? ret : orig; + } + + // Set the width or height on the element + jQuery( elem ).css( type, value ); + }, type, value, arguments.length, null ); + }; + }); + + -// 将 jQuery 暴露到全局对象 window 中 // Expose jQuery to the global object -window.jQuery = window.$ = jQuery; + window.jQuery = window.$ = jQuery; -// 将 jQuery 作为 AMD 模块暴露,但仅适用于理解在页面中加载多个 jQuery 版本问题的 AMD 加载器 // Expose jQuery as an AMD module, but only for AMD loaders that // understand the issues with loading multiple versions of jQuery // in a page that all might call define(). The loader will indicate @@ -9700,10 +9395,10 @@ window.jQuery = window.$ = jQuery; // file names, and jQuery is normally delivered in a lowercase file name. // Do this after creating the global so that if an AMD module wants to call // noConflict to hide this version of jQuery, it will work. -if ( typeof define === "function" && define.amd && define.amd.jQuery ) { - define( "jquery", [], function () { return jQuery; } ); -} + if ( typeof define === "function" && define.amd && define.amd.jQuery ) { + define( "jquery", [], function () { return jQuery; } ); + } -})( window ); \ No newline at end of file +})( window );