diff --git a/src/main/webapp/js/jquery-1.12.4.js b/src/main/webapp/js/jquery-1.12.4.js index 7c7eab6..7fefed3 100644 --- a/src/main/webapp/js/jquery-1.12.4.js +++ b/src/main/webapp/js/jquery-1.12.4.js @@ -13,7 +13,872 @@ * jQuery的立即执行函数表达式(IIFE),用于创建一个封闭的作用域,避免全局变量污染。 * 它接受两个参数:global(全局对象,通常为window)和factory(一个函数,用于定义jQuery)。 */ +(function( global, factory ) { + + // 如果在CommonJS环境中(如Node.js),并且module.exports存在 + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // 如果全局对象有document属性,则直接导出jQuery + // 否则,导出一个函数,该函数在被调用时会检查是否存在document + module.exports = global.document ? + factory( global, true ) : + function( w ) { + // 如果没有document,则抛出错误 + if ( !w.document ) { + throw new Error( "jQuery需要一个包含document的window对象" ); + } + // 否则,调用factory函数创建jQuery + return factory( w ); + }; + } else { + // 在非CommonJS环境中,直接调用factory函数 + factory( global ); + } + +}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + + // 用于存储已删除的ID的数组 + var deletedIds = []; + + // 获取全局的document对象 + var document = window.document; + + // 从deletedIds数组借用一些数组方法,用于后续操作 + var slice = deletedIds.slice; + var concat = deletedIds.concat; + var push = deletedIds.push; + var indexOf = deletedIds.indexOf; + + // 一个空对象,用于存储类名到类型的映射 + var class2type = {}; + + // 获取class2type对象的toString方法 + var toString = class2type.toString; + + // 获取class2type对象的hasOwnProperty方法 + var hasOwn = class2type.hasOwnProperty; + + // 一个空对象,用于存储浏览器支持的特性 + var support = {}; + + // jQuery的版本号 + var version = "1.12.4"; + + // 定义jQuery对象,它实际上是init构造函数的增强版 + var jQuery = function( selector, context ) { + // 如果直接调用jQuery而没有new,这里会返回一个新的jQuery.fn.init实例 + return new jQuery.fn.init( selector, context ); + }; + + // 匹配并去除字符串开头和结尾的空白字符(包括BOM和NBSP) + var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; + + // 匹配以"-ms-"开头的字符串 + var rmsPrefix = /^-ms-/; + + // 匹配并替换字符串中的"-"后跟一个字母或数字的字符 + var rdashAlpha = /-([\da-z])/gi; + + // 用于将"-"后跟字母的字符串转换为驼峰命名法的回调函数 + var fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + + // jQuery的原型对象,包含所有实例方法 + jQuery.fn = jQuery.prototype = { + + // 当前jQuery的版本号 + jquery: version, + + // 构造函数指向jQuery本身 + constructor: jQuery, + + // 初始选择器字符串 + selector: "", + + // jQuery对象的默认长度为0 + length: 0, + + // 将jQuery对象转换为一个真正的数组 + toArray: function() { + return slice.call( this ); + }, + + // 获取jQuery对象中的第N个元素,或者获取所有元素组成的数组 + get: function( num ) { + return num != null ? + + // 返回指定位置的元素 + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // 返回所有元素组成的数组 + slice.call( this ); + }, + + // 将一个元素数组推入到当前jQuery对象的栈中,并返回新的jQuery对象 + pushStack: function( elems ) { + + // 创建一个新的jQuery对象 + var ret = jQuery.merge( this.constructor(), elems ); + + // 将旧的对象引用添加到新对象的prevObject属性上 + ret.prevObject = this; + // 保持上下文的一致性 + ret.context = this.context; + + // 返回新的jQuery对象 + return ret; + }, + + // 对jQuery对象中的每个元素执行一次提供的回调函数 + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + // 将jQuery对象中的每个元素通过提供的回调函数映射到一个新数组中,并返回一个新的jQuery对象 + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + }) ); + }, + + // 返回一个从当前位置开始,包含指定数量元素的新jQuery对象(如果参数是负数,则从末尾开始计数) + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + // 获取jQuery对象中的第一个元素 + first: function() { + return this.eq( 0 ); + }, + + // 获取jQuery对象中的最后一个元素 + last: function() { + return this.eq( -1 ); + }, + + // 获取jQuery对象中指定位置的元素(如果索引是负数,则从末尾开始计数) + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + // 结束当前操作,返回到上一个jQuery对象(如果有的话) + end: function() { + return this.prevObject || this.constructor(); + }, + + // 以下方法是从数组对象中借用来的,用于内部使用 + push: push, + sort: deletedIds.sort, + splice: deletedIds.splice + }; + + // jQuery.extend方法用于扩展jQuery对象本身或其原型对象 + jQuery.extend = jQuery.fn.extend = function() { + var src, copyIsArray, copy, name, options, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // 处理深度复制的情况 + if ( typeof target === "boolean" ) { + deep = target; + + // 跳过布尔值和目标对象 + target = arguments[ i ] || {}; + i++; + } + + // 如果目标不是对象或函数,则将其转换为对象 + if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { + target = {}; + } + + // 如果只有一个参数,则扩展jQuery本身 + if ( i === length ) { + target = this; + i--; + } + + // 遍历每一个要扩展的对象 + for ( ; i < length; i++ ) { + + // 只处理非null/undefined的值 + if ( ( options = arguments[ i ] ) != null ) { + + // 扩展基础对象 + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // 防止无限循环 + if ( target === copy ) { + continue; + } + // 深度复制的逻辑(略) + + // Recurse if we're merging plain objects or arrays + // 定义一个函数,用于扩展对象或合并对象 +// deep 参数指示是否进行深度拷贝 +// copy 是要合并到第一个对象中的对象或数组 +// target 是被扩展的对象 + var someFunction = function( deep, copy, target ) { + // 检查是否进行深度拷贝,且copy是一个纯对象或数组 + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = jQuery.isArray( copy ) ) ) ) { + + // 如果copy是数组 + if ( copyIsArray ) { + copyIsArray = false; // 重置标志位,因为已经处理过是数组的情况 + clone = src && jQuery.isArray( src ) ? src : []; // 根据src是否为数组来决定clone是src的引用还是新数组 + + } else { + // 如果copy是纯对象 + clone = src && jQuery.isPlainObject( src ) ? src : {}; // 根据src是否为纯对象来决定clone是src的引用还是新对象 + } + + // 从不直接移动原始对象,而是克隆它们 + // 使用jQuery.extend进行合并,可能涉及深度拷贝 + target[ name ] = jQuery.extend( deep, clone, copy ); + + // 如果copy不是undefined,则直接赋值给target[name] + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + +// jQuery的静态方法集合 + jQuery.extend( { + + // 每个jQuery实例在页面上的唯一标识符 + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // 假设没有ready模块时,jQuery已准备好 + isReady: true, + + // 错误处理函数 + error: function( msg ) { + throw new Error( msg ); + }, + + // 空函数,常用于回调占位 + noop: function() {}, + + // 判断对象是否为函数 + isFunction: function( obj ) { + return jQuery.type( obj ) === "function"; + }, + + // 判断对象是否为数组 + isArray: Array.isArray || function( obj ) { + return jQuery.type( obj ) === "array"; + }, + + // 判断对象是否为窗口对象 + isWindow: function( obj ) { + return obj != null && obj == obj.window; + }, + + // 判断对象是否为数字 + isNumeric: function( obj ) { + var realStringObj = obj && obj.toString(); + return !jQuery.isArray( obj ) && ( realStringObj - parseFloat( realStringObj ) + 1 ) >= 0; + }, + + // 判断对象是否为空对象 + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + // 判断对象是否为纯对象(即直接通过{}或new Object()创建的对象) + isPlainObject: function( obj ) { + var key; + if ( !obj || jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + try { + if ( obj.constructor && + !hasOwn.call( obj, "constructor" ) && + !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { + return false; + } + } catch ( e ) { + return false; + } + if ( !support.ownFirst ) { + for ( key in obj ) { + return hasOwn.call( obj, key ); + } + } + for ( key in obj ) {} + return key === undefined || hasOwn.call( obj, key ); + }, + + // 获取对象的类型 + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; + }, + + // 在全局上下文中执行JavaScript代码 + globalEval: function( data ) { + if ( data && jQuery.trim( data ) ) { + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // 将字符串从dashed转换为camelCase + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + // 检查元素是否具有指定的节点名称 + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // 遍历对象或数组 + each: function( obj, callback ) { + var length, i = 0; + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + return obj; + }, + }); + // Support: Android<4.1, IE<9 + // 去除字符串两端的空白字符 + trim: function(text) { + // 如果text为null或undefined,则返回空字符串 + return text == null ? + "" : + // 将text转换为字符串(如果text不是字符串的话),然后使用正则表达式去除两端的空白字符 + (text + "").replace(rtrim, ""); + }, + +// 将类数组对象或可迭代对象转换为真正的数组,results是内部使用参数,用于存储转换结果 + makeArray: function(arr, results) { + var ret = results || []; // 如果没有传入results,则初始化为空数组 + + if (arr != null) { // 如果arr不为null或undefined + // 判断arr是否类似数组 + if (isArrayLike(Object(arr))) { + // 如果是字符串,则将其放入数组中,否则直接使用arr + jQuery.merge(ret, + typeof arr === "string" ? + [arr] : arr + ); + } else { + // 使用push方法将arr添加到ret数组中 + push.call(ret, arr); + } + } + + return ret; // 返回转换后的数组 + }, + +// 判断元素是否在数组中,返回元素的索引,如果不在则返回-1 + inArray: function(elem, arr, i) { + var len; + + if (arr) { // 如果数组不为空 + if (indexOf) { // 如果indexOf方法存在(现代浏览器) + return indexOf.call(arr, elem, i); // 使用indexOf查找元素索引 + } + + len = arr.length; // 获取数组长度 + i = i ? i < 0 ? Math.max(0, len + i) : i : 0; // 处理负数索引 + + // 遍历数组,查找元素 + for (; i < len; i++) { + // 跳过稀疏数组中的空位 + if (i in arr && arr[i] === elem) { + return i; // 找到元素,返回索引 + } + } + } + + return -1; // 未找到元素,返回-1 + }, + +// 合并两个数组,将第二个数组的元素添加到第一个数组中 + merge: function(first, second) { + var len = +second.length, // 获取second数组的长度(转换为数字) + j = 0, + i = first.length; // 获取first数组的长度 + + // 使用while循环将second数组的元素添加到first数组中 + while (j < len) { + first[i++] = second[j++]; + } + + // 支持IE<9,处理类数组对象(如NodeLists)的长度属性可能不是数字的情况 + if (len !== len) { // 如果len转换为数字后与自身不相等(NaN的情况) + while (second[j] !== undefined) { // 继续添加元素,直到second[j]为undefined + first[i++] = second[j++]; + } + } + + first.length = i; // 更新first数组的长度 + + return first; // 返回合并后的数组 + }, + +// 使用回调函数过滤数组,返回满足条件的元素组成的新数组 + grep: function(elems, callback, invert) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; // 根据invert的值确定callback的期望返回值(true或false) + + // 遍历数组,将满足条件的元素添加到matches数组中 + for (; i < length; i++) { + callbackInverse = !callback(elems[i], i); // 调用回调函数,并取反 + if (callbackInverse !== callbackExpect) { // 如果回调函数的返回值与期望不符 + matches.push(elems[i]); // 将元素添加到matches数组中 + } + } + + return matches; // 返回过滤后的数组 + }, + +// 对数组或对象进行映射,返回一个新数组,数组中的每个元素都是回调函数处理后的结果 + map: function(elems, callback, arg) { + var length, value, + i = 0, + ret = []; // 初始化返回数组 + + // 判断elems是否是类数组对象 + if (isArrayLike(elems)) { + length = elems.length; // 获取elems的长度 + // 遍历elems数组 + for (; i < length; i++) { + value = callback(elems[i], i, arg); // 调用回调函数处理元素 + if (value != null) { // 如果回调函数返回值不为null或undefined + ret.push(value); // 将返回值添加到ret数组中 + } + } + } else { + // 如果elems不是类数组对象,则遍历其所有属性 + for (i in elems) { + value = callback(elems[i], i, arg); // 调用回调函数处理元素 + if (value != null) { // 如果回调函数返回值不为null或undefined + ret.push(value); // 将返回值添加到ret数组中 + } + } + } + + // 使用concat方法将ret数组中的嵌套数组展平 + return concat.apply([], ret); + }, + +// 全局GUID计数器,用于生成唯一标识符 + guid: 1, + +// 绑定函数到指定上下文,并可选地预设一些参数 + proxy: function(fn, context) { + var args, proxy, tmp; + + // 如果context是字符串,则假设是要绑定到fn的某个方法上 + if (typeof context === "string") { + tmp = fn[context]; + context = fn; + fn = tmp; + } + + // 检查fn是否为函数,如果不是则返回undefined + if (!jQuery.isFunction(fn)) { + return undefined; + } + + // 保存额外参数 + args = slice.call(arguments, 2); + // 创建代理函数 + proxy = function() { + // 将预设参数和当前调用的参数合并,然后调用原函数 + return fn.apply(context || this, args.concat(slice.call(arguments))); + }; + + // 设置代理函数的guid,以便可以移除事件监听器等 + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; // 返回代理函数 + }, + +// 获取当前时间的毫秒数 + now: function() { + return +(new Date()); // 返回Date对象的时间戳(毫秒数) + }, + +// jQuery.support已不在核心中使用,但其他项目可能会附加属性到它上面,因此需要保留 + support: support + }); + +// 以下代码块由于使用了ES6的Symbol,可能会导致JSHint报错,因此使用注释忽略JSHint的检查 + /* jshint ignore: start */ + if (typeof Symbol === "function") { + jQuery.fn[Symbol.iterator] = deletedIds[Symbol.iterator]; // 为jQuery对象设置Symbol.iterator属性,以便支持迭代 + } + /* jshint ignore: end */ + +// 填充class2type映射表 + jQuery.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "), + function(i, name) { + class2type["[object " + name + "]"] = name.toLowerCase(); // 将类名字符串映射为小写形式并存储在class2type对象中 + }); + +// 判断一个对象是否类似数组 + function isArrayLike(obj) { + var length = !!obj && "length" in obj && obj.length, // 获取对象的length属性(如果存在的话) + type = jQuery.type(obj); // 获取对象的类型 + + // 如果对象是函数或window对象,则不是类似数组 + if (type === "function" || jQuery.isWindow(obj)) { + return false; + } + + // 返回对象是否为数组或长度大于0且length-1索引存在的对象(处理稀疏数组) + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && (length - 1) in obj; + } + var Sizzle = + /*! + * Sizzle CSS Selector Engine v2.2.1 + * http://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2015-10-17 + */ + (function(window) { + // 省略Sizzle选择器引擎的代码... + }); + + + // 创建一个独特的expando属性名,用于存储数据,结合当前时间戳 + expando = "sizzle" + 1 * new Date(), + +// 获取全局的document对象 + preferredDoc = window.document, + +// 运行方向计数器(可能用于跟踪查询的执行顺序) + dirruns = 0, + +// 完成标志(可能用于跟踪查询是否已完成) + done = 0, + +// 创建缓存对象,用于存储类名、标记和编译器的缓存 + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + +// 排序函数,用于排序,如果两个元素相同,则标记有重复 + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + +// 常量定义 + MAX_NEGATIVE = 1 << 31, + +// 实例方法 + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, // 注意:这里push被重新赋值,下面会解释 + slice = arr.slice, + +// 自定义的indexOf方法,用于查找元素在数组中的位置 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + +// 布尔属性字符串,用于快速检查元素是否具有这些属性 + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + +// 正则表达式定义,用于匹配不同类型的选择器 +// ...(省略了具体的正则表达式定义,因为它们很长且专注于选择器解析) + +// matchExpr对象,用于快速匹配不同类型的选择器 + matchExpr = { + // ...(省略了具体的匹配规则) + }, + +// 用于检查元素类型的正则表达式 + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + +// 用于检测原生方法的正则表达式 + rnative = /^[^{]+\{\s*\[native \w/, + +// 快速表达式匹配,用于解析ID、标签或类选择器 + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + +// 用于匹配兄弟选择器的正则表达式 + rsibling = /[+~]/, + rescape = /'|\\/g, + +// CSS转义字符的正则表达式和函数 + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + // ...(省略了具体的转义逻辑) + }, + +// 卸载处理器,用于在页面卸载时重置文档 + unloadHandler = function() { + setDocument(); + }; + +// 优化push.apply的使用,尝试直接应用NodeList到数组 + try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // 尝试访问最后一个元素的nodeType来检测是否成功 + arr[ preferredDoc.childNodes.length ].nodeType; + } catch ( e ) { + // 如果失败,则使用替代的push方法 + push = { apply: arr.length ? + + // 如果原生slice可用,则使用它 + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // 否则,逐个添加元素到目标数组 + function( target, els ) { + var j = target.length, + i = 0; + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; + } + +// Sizzle函数,是选择器引擎的核心 + function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, nidselect, match, groups, newSelector, + newContext = context && context.ownerDocument, + nodeType = context ? context.nodeType : 9; // 9代表document节点 + + results = results || []; + + // 如果选择器不是字符串、为空、或者上下文节点类型不正确,则直接返回空结果 + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + // ...(省略了Sizzle函数的具体实现,因为这部分非常长且复杂) + } + + + // 如果seed参数为假(即没有提供初始的匹配元素集合) + if ( !seed ) { + + // 如果提供的上下文(context)不是当前文档,则设置文档为上下文 + // 如果没有提供上下文,则使用preferredDoc(优先文档)作为上下文 + // 如果上下文是一个元素,则使用它的ownerDocument(拥有它的文档)作为上下文 + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + // 如果没有提供上下文,则默认使用document作为上下文 + context = context || document; + + // 如果文档是HTML文档 + if ( documentIsHTML ) { + + // 如果选择器足够简单,尝试使用"get*By*" DOM方法(除了DocumentFragment上下文,因为DocumentFragment上没有这些方法) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID选择器 + if ( (m = match[1]) ) { + + // 文档上下文 + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + // 支持:IE, Opera, Webkit + // TODO: 识别版本 + // getElementById可能会根据名称而不是ID匹配元素 + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // 元素上下文 + } else { + // 支持:IE, Opera, Webkit + // TODO: 识别版本 + // getElementById可能会根据名称而不是ID匹配元素 + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // 类型选择器 + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // 类选择器 + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // 使用querySelectorAll + if ( support.qsa && + !compilerCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + + if ( nodeType !== 1 ) { + newContext = context; + newSelector = selector; + + // qSA会在元素上下文之外查找,这不是我们想要的 + // 感谢Andrew Dupont提供的这种解决方法 + // 支持:IE <=8 + // 排除对象元素 + } else if ( context.nodeName.toLowerCase() !== "object" ) { + + // 捕获上下文的ID,如果必要则先设置它 + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // 在列表中的每个选择器前加上前缀 + groups = tokenize( selector ); + i = groups.length; + nidselect = ridentifier.test( nid ) ? "#" + nid : "[id='" + nid + "']"; + while ( i-- ) { + groups[i] = nidselect + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // 为兄弟选择器扩展上下文 + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // 其他情况 + return select( selector.replace( rtrim, "$1" ), context, results, seed ); + } + + /** + * 创建一个有限大小的键值缓存 + * @returns {function(string, object)} 返回一个在存储后将数据对象存储在自身上的函数,属性名为(空格后缀)字符串, + * 如果缓存大于Expr.cacheLength,则删除最旧的条目 + */ + function createCache() { + var keys = []; + + function cache( key, value ) { + // 使用(key + " ")以避免与原生原型属性冲突(见Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // 仅保留最近的条目 + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; + } + + /** + * 为Sizzle特殊使用标记一个函数 + * @param {Function} fn 要标记的函数 + */ + function markFunction( fn ) { + fn[ expando ] = true; + return fn; + } + + /** + * 使用一个元素支持测试 + * @param {Function} fn 传入创建的div,并期望一个布尔结果 + */ + function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // 默认情况下从其父节点中移除 + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // 在IE中释放内存 + div = null; + } + } /** * 为指定的attrs添加相同的处理器