From 2e4f4cd1e4c1ee2147c96b2a1310bf08920d5dcd Mon Sep 17 00:00:00 2001 From: LFE <756741044@qq.com> Date: Fri, 13 Dec 2024 22:15:16 +0800 Subject: [PATCH 1/3] lfe --- src/com/yj/bean/Cart.java | 75 ++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/src/com/yj/bean/Cart.java b/src/com/yj/bean/Cart.java index 3ba99bf..782eac6 100644 --- a/src/com/yj/bean/Cart.java +++ b/src/com/yj/bean/Cart.java @@ -13,93 +13,128 @@ import java.util.Map; * 购物车对象 */ public class Cart { + // 注释掉的代码表示之前可能考虑过直接存储总数和总价,但在此实现中,选择动态计算这些值 //private Integer totalCount; //private BigDecimal totalPrice; + /** + * 获取购物车中所有商品的总数量 + */ public Integer getTotalCount() { Integer totalCount = 0; + // 初始化总数量为0 for (Map.Entryentry : items.entrySet()) { + // 遍历购物车中的每一项 totalCount += entry.getValue().getCount(); + // 累加每一项的数量 } return totalCount; + // 返回总数量 } - + /** + * 获取购物车中所有商品的总价格 + */ public BigDecimal getTotalPrice() { BigDecimal totalPrice = new BigDecimal(0); + // 初始化总价格为0 for (Map.Entryentry : items.entrySet()) { + // 遍历购物车中的每一项 totalPrice = totalPrice.add(entry.getValue().getTotalPrice()); + // 累加每一项的总价格 } return totalPrice; + // 返回总价格 } - + /** + * 获取购物车中的所有商品项 + */ public Map getItems() { return items; + // 返回购物车中的商品项 } + /** + * 设置购物车中的所有商品项 + */ public void setItems(Map items) { this.items = items; + // 设置购物车中的商品项 } /** - * key是商品编号,value是商品信息 + * 商品项存储的Map,key是商品编号,value是商品信息,使用LinkedHashMap保持插入顺序 */ private Map items = new LinkedHashMap(); + /** + * 重写toString方法,用于打印购物车信息 + */ @Override public String toString() { return "Cart{" + "totalCount=" + getTotalCount() + + // 总数量 ", totalPrice=" + getTotalPrice() + + // 总价格 ", items=" + items + + // 商品项 '}'; } /** - * 添加商品项 - * @param cartItem + * 添加商品项到购物车 + * @param cartItem 要添加的商品项 */ public void addItem(CartItem cartItem) { - //先查看购物车中是否包含次商品,如果有的话,数量更新,总金额更新;如果没有,直接放到集合中即可 + // 先查看购物车中是否包含此商品 CartItem item = items.get(cartItem.getId()); if(item == null) { - //之前没有添加过此商品 + // 之前没有添加过此商品,直接添加到购物车中 items.put(cartItem.getId(),cartItem); } else { - item.setCount(item.getCount() + 1);//数量累计 - item.setTotalPrice(item.getPrice().multiply(new BigDecimal(item.getCount())));//更新总金额 + // 购物车中已有此商品,数量加1,并更新总金额 + item.setCount(item.getCount() + 1); + // 数量累计 + item.setTotalPrice(item.getPrice().multiply(new BigDecimal(item.getCount()))); + // 更新总金额 } } /** - * 删除商品项 - * @param id + * 从购物车中删除指定编号的商品项 + * @param id 要删除的商品项的编号 */ public void deleteItem(Integer id) { items.remove(id); + // 从购物车中删除指定编号的商品项 } /** - * 清空购物车 + * 清空购物车中的所有商品项 */ public void clear() { items.clear(); + // 清空购物车 } /** - * 修改商品数量 - * @param id - * @param count + * 修改购物车中指定编号的商品项的数量 + * @param id 商品项的编号 + * @param count 新的数量 */ public void updateCount(Integer id,Integer count) { - //先查看购物车中是否包含次商品,如果有的话,数量更新,总金额更新; + // 先查看购物车中是否包含此商品项 CartItem cartItem = items.get(id); - if(cartItem != null) { - cartItem.setCount(count); - cartItem.setTotalPrice(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount()))); + if(cartItem != null) { + // 购物车中已有此商品项,修改数量并更新总金额 + cartItem.setCount(count); + // 修改数量 + cartItem.setTotalPrice(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount()))); + // 更新总金额 } } -} +} \ No newline at end of file From df7b5a641e1170b30234be9290e0bc6a77ee1edc Mon Sep 17 00:00:00 2001 From: LFE <756741044@qq.com> Date: Fri, 13 Dec 2024 23:00:51 +0800 Subject: [PATCH 2/3] lfe --- src/com/yj/bean/CartItem.java | 49 ++++- src/com/yj/bean/Page.java | 36 +++- src/com/yj/dao/impl/BaseDao.java | 91 +++++++--- src/com/yj/test/CartTest.java | 20 ++ src/com/yj/test/JDBCUtilsTest.java | 23 ++- src/com/yj/utils/JDBCUtils.java | 102 +++++++---- src/com/yj/utils/PaymentUtil.java | 90 +++++---- src/com/yj/web/CartServlet.java | 121 ++++++------ web/pages/cart/cart.jsp | 121 +++++++----- web/pages/cart/checkout.jsp | 58 ++++-- web/pages/cart/pay.jsp | 60 ++++-- web/static/script/jquery-1.7.2.js | 283 ++++++++++++++++++----------- 12 files changed, 690 insertions(+), 364 deletions(-) diff --git a/src/com/yj/bean/CartItem.java b/src/com/yj/bean/CartItem.java index c9feca1..bcc656f 100644 --- a/src/com/yj/bean/CartItem.java +++ b/src/com/yj/bean/CartItem.java @@ -10,72 +10,101 @@ import java.math.BigDecimal; /** * 购物车商品项 */ +// 定义一个名为CartItem的类,代表购物车中的一个商品项 public class CartItem { + // 定义一个私有变量id,用于存储商品的唯一标识符 private Integer id; + // 定义一个私有变量name,用于存储商品的名称 private String name; + // 定义一个私有变量count,用于存储商品的数量 private Integer count; + // 定义一个私有变量price,用于存储商品的单价,使用BigDecimal类型以支持精确的小数计算 private BigDecimal price; + // 定义一个私有变量totalPrice,用于存储商品的总价(单价乘以数量),同样使用BigDecimal类型 private BigDecimal totalPrice; + // 定义一个公开的getter方法,用于获取商品的id public Integer getId() { return id; } - + // 定义一个公开的setter方法,用于设置商品的id public void setId(Integer id) { this.id = id; } - + // 定义一个公开的getter方法,用于获取商品的名称 public String getName() { return name; } - + // 定义一个公开的setter方法,用于设置商品的名称 public void setName(String name) { this.name = name; } - + // 定义一个公开的getter方法,用于获取商品的数量 public Integer getCount() { return count; } - + // 定义一个公开的setter方法,用于设置商品的数量 public void setCount(Integer count) { this.count = count; } - + // 定义一个公开的getter方法,用于获取商品的单价 public BigDecimal getPrice() { return price; } - + // 定义一个公开的setter方法,用于设置商品的单价 public void setPrice(BigDecimal price) { this.price = price; } - + // 定义一个公开的getter方法,用于获取商品的总价 public BigDecimal getTotalPrice() { return totalPrice; } - + // 定义一个公开的setter方法,用于设置商品的总价 public void setTotalPrice(BigDecimal totalPrice) { this.totalPrice = totalPrice; } + // 定义一个公开的构造方法,用于创建CartItem对象并初始化其所有属性 public CartItem(Integer id, String name, Integer count, BigDecimal price, BigDecimal totalPrice) { + // 使用this关键字将传入的id参数赋值给当前对象的id属性 this.id = id; + // 设置商品的唯一标识符 + + // 使用this关键字将传入的name参数赋值给当前对象的name属性 this.name = name; + // 设置商品的名称 + + // 使用this关键字将传入的count参数赋值给当前对象的count属性 this.count = count; + // 设置商品的数量 + + // 使用this关键字将传入的price参数赋值给当前对象的price属性 this.price = price; + // 设置商品的单价,使用BigDecimal类型以支持精确的小数计算 + + // 使用this关键字将传入的totalPrice参数赋值给当前对象的totalPrice属性 this.totalPrice = totalPrice; + // 设置商品的总价(单价乘以数量),同样使用BigDecimal类型 } - + // 定义一个无参构造方法,用于创建CartItem对象时不初始化任何属性 public CartItem() { } + // 重写toString方法,用于返回CartItem对象的字符串表示形式 @Override public String toString() { + // 返回的字符串包含CartItem对象的所有属性信息 return "Cart{" + "id=" + id + + // 显示商品的id ", name='" + name + '\'' + + // 显示商品的名称 ", count=" + count + + // 显示商品的数量 ", price=" + price + + // 显示商品的单价 ", totalPrice=" + totalPrice + + // 显示商品的总价 '}'; } } diff --git a/src/com/yj/bean/Page.java b/src/com/yj/bean/Page.java index 5869cab..ed5f155 100644 --- a/src/com/yj/bean/Page.java +++ b/src/com/yj/bean/Page.java @@ -1,90 +1,118 @@ package com.yj.bean; +// 定义包名,用于组织相关的类和接口 import java.util.List; +// 导入List接口,用于存储数据集合 /** - * @author yj - * @create 2020-08-25 9:27 + * @author yj // 类的作者 + * @create 2020-08-25 9:27 // 类的创建时间 */ public class Page { + // 定义一个泛型类Page,T表示该Page类可以持有任何类型的对象 public static final Integer PAGE_SIZE = 4; + // 定义一个常量,表示每页显示的数据条数 //当前页 private Integer pageNo; + // 当前页码 //总页码 private Integer pageTotal; + // 总页数 //当前页显示数量 private Integer pageSize = PAGE_SIZE; + // 每页显示的数据条数,默认为常量PAGE_SIZE的值 //总的记录数 private Integer pageTotalCount; + // 数据总条数 //当前页数据 private List items; + // 存储当前页的数据 //分页条的请求地址 private String url; + // 分页条请求的URL地址 + // 获取分页条请求的URL地址 public String getUrl() { return url; } + // 设置分页条请求的URL地址 public void setUrl(String url) { this.url = url; } + // 获取当前页码 public Integer getPageNo() { return pageNo; } - + // 获取总页数 public Integer getPageTotal() { return pageTotal; } + // 设置总页数 public void setPageTotal(Integer pageTotal) { this.pageTotal = pageTotal; } + // 获取每页显示的数据条数 public Integer getPageSize() { return pageSize; } + // 设置每页显示的数据条数 public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } + // 获取数据总条数 public Integer getPageTotalCount() { return pageTotalCount; } + // 设置数据总条数 public void setPageTotalCount(Integer pageTotalCount) { this.pageTotalCount = pageTotalCount; } + // 获取当前页的数据 public List getItems() { return items; } + // 设置当前页的数据 public void setItems(List items) { this.items = items; } + // 设置当前页码 public void setPageNo(Integer pageNo) { this.pageNo = pageNo; } + // 重写toString方法,用于打印Page对象的详细信息 @Override public String toString() { return "Page{" + "pageNo=" + pageNo + + // 当前页码 ", pageTotal=" + pageTotal + + // 总页数 ", pageSize=" + pageSize + + // 每页显示的数据条数 ", pageTotalCount=" + pageTotalCount + + // 数据总条数 ", items=" + items + + // 当前页的数据 ", url='" + url + '\'' + + // 分页条请求的URL地址 '}'; } -} +} \ No newline at end of file diff --git a/src/com/yj/dao/impl/BaseDao.java b/src/com/yj/dao/impl/BaseDao.java index 60a0e28..4a215d7 100644 --- a/src/com/yj/dao/impl/BaseDao.java +++ b/src/com/yj/dao/impl/BaseDao.java @@ -1,85 +1,124 @@ package com.yj.dao.impl; +// 定义包名,组织相关的类 import com.yj.utils.JDBCUtils; +// 导入JDBC工具类,用于获取数据库连接 import org.apache.commons.dbutils.QueryRunner; +// 导入DbUtils的QueryRunner类,用于简化JDBC操作 import org.apache.commons.dbutils.handlers.BeanHandler; +// 导入BeanHandler,用于将结果集的第一行数据封装为JavaBean对象 import org.apache.commons.dbutils.handlers.BeanListHandler; +// 导入BeanListHandler,用于将结果集的每一行数据封装为JavaBean对象,并存储在List集合中 import org.apache.commons.dbutils.handlers.ScalarHandler; +// 导入ScalarHandler,用于返回结果集的一行一列数据 import java.sql.Connection; +// 导入Connection接口,用于数据库连接 import java.sql.SQLException; +// 导入SQLException类,处理SQL异常 import java.util.List; +// 导入List接口,用于存储数据集合 /** - * @author yj - * @create 2020-08-21 11:32 + * @author yj // 类的作者 + * @create 2020-08-21 11:32 // 类的创建时间 */ public abstract class BaseDao { - //使用DbUtils操作数据库 + // 定义一个抽象类BaseDao,作为数据访问层的基类 + // 使用DbUtils操作数据库 private QueryRunner queryRunner = new QueryRunner(); - /* - *update()方法用来执行Insert\Update\Delete语句 + // 初始化QueryRunner对象,用于执行SQL语句 + + /** + * update()方法用来执行Insert\Update\Delete语句 + * @param sql 要执行的SQL语句 + * @param args SQL语句中的参数值,可变参数 * @return 如果返回-1则失败,成功返回影响的行数 */ - public int update(String sql,Object ... args) { + public int update(String sql, Object ... args) { Connection connection = JDBCUtils.getConnection(); + // 调用JDBCUtils工具类获取数据库连接 try { - return queryRunner.update(connection,sql,args); + // 使用QueryRunner的update方法执行更新操作,并返回影响的行数 + return queryRunner.update(connection, sql, args); } catch (SQLException e) { e.printStackTrace(); + // 打印SQL异常堆栈信息 throw new RuntimeException(e); + // 将SQL异常转换为运行时异常抛出 + } finally { + // 注意:原代码中缺少了关闭连接的逻辑,应在此处添加(但在实际使用中,JDBCUtils的getConnection可能已负责关闭) + // JDBCUtils.close(connection); // 关闭数据库连接 } } /** - *查询返回一个Javabean的SQL语句 + * 查询返回一个Javabean的SQL语句 * @param type 返回的对象类型 - * @param sql 执行的sql语句 - * @param args sql对应的参数值 + * @param sql 执行的SQL语句 + * @param args SQL语句对应的参数值,可变参数 * @param 返回的类型的泛型 - * @return + * @return 返回查询结果的第一行数据封装成的JavaBean对象 */ - public T queryForOne(Class type,String sql,Object ... args) { + public T queryForOne(Class type, String sql, Object ... args) { Connection connection = JDBCUtils.getConnection(); + // 获取数据库连接 try { - return queryRunner.query(connection,sql,new BeanHandler(type),args); + // 使用QueryRunner的query方法执行查询操作,并将结果集的第一行数据封装为指定的JavaBean对象 + return queryRunner.query(connection, sql, new BeanHandler(type), args); } catch (SQLException e) { e.printStackTrace(); + // 打印SQL异常堆栈信息 throw new RuntimeException(e); + // 将SQL异常转换为运行时异常抛出 + } finally { + // 注意:同样缺少了关闭连接的逻辑,应在实际使用中添加 } } /** - *查询返回多个Javabean的SQL语句 + * 查询返回多个Javabean的SQL语句 * @param type 返回的对象类型 - * @param sql 执行的sql语句 - * @param args sql对应的参数值 + * @param sql 执行的SQL语句 + * @param args SQL语句对应的参数值,可变参数 * @param 返回的类型的泛型 - * @return + * @return 返回查询结果的每一行数据封装成的JavaBean对象列表 */ public List queryForList(Class type, String sql, Object ... args) { Connection connection = JDBCUtils.getConnection(); + // 获取数据库连接 try { - return queryRunner.query(connection,sql,new BeanListHandler(type),args); + // 使用QueryRunner的query方法执行查询操作,并将结果集的每一行数据封装为指定的JavaBean对象,存储在List集合中返回 + return queryRunner.query(connection, sql, new BeanListHandler(type), args); } catch (SQLException e) { e.printStackTrace(); + // 打印SQL异常堆栈信息 throw new RuntimeException(e); + // 将SQL异常转换为运行时异常抛出 + } finally { + // 注意:同样缺少了关闭连接的逻辑,应在实际使用中添加 } } /** - * 执行返回一行一列的sql语句 - * @param sql 执行的sql语句 - * @param args sql对应的参数值 - * @return + * 执行返回一行一列的SQL语句 + * @param sql 执行的SQL语句 + * @param args SQL语句对应的参数值,可变参数 + * @return 返回查询结果的一行一列数据 */ - public Object queryForSingleValue(String sql,Object ... args) { + public Object queryForSingleValue(String sql, Object ... args) { Connection connection = JDBCUtils.getConnection(); + // 获取数据库连接 try { - return queryRunner.query(connection,sql,new ScalarHandler(),args); - }catch (SQLException e) { + // 使用QueryRunner的query方法执行查询操作,并返回结果集的一行一列数据 + return queryRunner.query(connection, sql, new ScalarHandler(), args); + } catch (SQLException e) { e.printStackTrace(); + // 打印SQL异常堆栈信息 throw new RuntimeException(e); + // 将SQL异常转换为运行时异常抛出 + } finally { + // 注意:同样缺少了关闭连接的逻辑,但在实际中,JDBCUtils的getConnection可能已负责关闭连接 } } -} +} \ No newline at end of file diff --git a/src/com/yj/test/CartTest.java b/src/com/yj/test/CartTest.java index f4b6506..7562506 100644 --- a/src/com/yj/test/CartTest.java +++ b/src/com/yj/test/CartTest.java @@ -10,44 +10,64 @@ import java.math.BigDecimal; * @author yj * @create 2020-08-26 20:30 */ +// 定义一个测试类CartTest,用于测试Cart类的功能 public class CartTest { + // 使用@Test注解标记的方法表示这是一个测试方法,通常与测试框架(如JUnit)一起使用 @Test public void addItem() { + // 创建一个新的Cart对象 Cart cart = new Cart(); + // 向购物车中添加一个商品项,注意这里totalPrice应该是根据price和count计算得出的,但此处直接给出 cart.addItem(new CartItem(1,"lkjs",1,new BigDecimal(5),new BigDecimal(66))); + // 再次添加相同的商品项,理论上应该更新数量而不是添加新项(但根据代码,它会添加新项) cart.addItem(new CartItem(1,"lkjs",1,new BigDecimal(5),new BigDecimal(66))); + // 添加一个不同的商品项 cart.addItem(new CartItem(2,"你妹的",1,new BigDecimal(5),new BigDecimal(66))); + // 打印购物车的内容,输出将取决于Cart类的toString方法实现 System.out.println(cart); } + // 另一个测试方法,用于测试删除商品项的功能 @Test public void deleteItem() { + // 创建一个新的Cart对象并添加一些商品项 Cart cart = new Cart(); cart.addItem(new CartItem(1,"lkjs",1,new BigDecimal(5),new BigDecimal(66))); cart.addItem(new CartItem(1,"lkjs",1,new BigDecimal(5),new BigDecimal(66))); cart.addItem(new CartItem(2,"你妹的",1,new BigDecimal(5),new BigDecimal(66))); + // 根据商品ID删除商品项,理论上应该删除所有ID匹配的项 cart.deleteItem(1); + // 打印购物车的内容,查看删除操作的结果 System.out.println(cart); } + // 测试清空购物车的方法 @Test public void clear() { + // 创建一个新的Cart对象并添加一些商品项 Cart cart = new Cart(); cart.addItem(new CartItem(1,"lkjs",1,new BigDecimal(5),new BigDecimal(66))); cart.addItem(new CartItem(1,"lkjs",1,new BigDecimal(5),new BigDecimal(66))); cart.addItem(new CartItem(2,"你妹的",1,new BigDecimal(5),new BigDecimal(66))); + // 清空购物车中的所有商品项 cart.clear(); + // 打印购物车的内容,应该为空 System.out.println(cart); } + // 测试更新商品数量的方法 @Test public void updateCount() { + // 创建一个新的Cart对象并添加一些商品项 Cart cart = new Cart(); cart.addItem(new CartItem(1,"lkjs",1,new BigDecimal(5),new BigDecimal(66))); cart.addItem(new CartItem(1,"lkjs",1,new BigDecimal(5),new BigDecimal(66))); cart.addItem(new CartItem(2,"你妹的",1,new BigDecimal(5),new BigDecimal(66))); + // 根据商品ID更新商品数量,注意这里可能需要根据业务逻辑决定是更新现有项还是添加新项 cart.updateCount(1,5); + // 假设意图是将ID为1的商品数量更新为5 + // 打印购物车的内容,查看更新操作的结果 System.out.println(cart); } } \ No newline at end of file diff --git a/src/com/yj/test/JDBCUtilsTest.java b/src/com/yj/test/JDBCUtilsTest.java index 3c762af..546fbd6 100644 --- a/src/com/yj/test/JDBCUtilsTest.java +++ b/src/com/yj/test/JDBCUtilsTest.java @@ -1,29 +1,44 @@ package com.yj.test; +// 声明包名,用于组织类 import com.yj.utils.JDBCUtils; +// 导入JDBCUtils工具类,用于数据库连接 import org.junit.Test; +// 导入JUnit测试注解 import java.sql.Connection; +// 导入Connection接口,用于数据库连接 import java.sql.ResultSet; +// 导入ResultSet接口,用于处理查询结果集 import java.sql.Statement; +// 导入Statement接口,用于执行SQL语句 /** * @author yj * @create 2020-08-21 11:25 */ -public class JDBCUtilsTest { - @Test +public class JDBCUtilsTest { // 定义一个测试类 + @Test // 标记此方法为一个测试方法 public void testJdbcUtils() { + // 定义一个测试方法 Connection connection = JDBCUtils.getConnection(); + // 通过JDBCUtils工具类获取数据库连接 String sql = "select * from t_book"; + // 定义SQL查询语句,查询t_book表中的所有记录 try (Statement st = connection.createStatement()) { + // 创建Statement对象用于执行SQL语句,使用try-with-resources语法自动关闭资源 ResultSet rs = st.executeQuery(sql); - while(rs.next()){ + // 执行查询语句,并将结果存储在ResultSet对象中 + while(rs.next()){ // 遍历ResultSet对象中的每一条记录 System.out.println(rs.getString("author")+" " + // 从当前记录中获取"author"字段的值,并输出 +rs.getString("name")); + // 从当前记录中获取"name"字段的值,并输出,与"author"字段的值用空格分隔 } }catch (Exception e) { + // 捕获并处理异常 e.printStackTrace(); + // 打印异常堆栈信息 } } -} +} \ No newline at end of file diff --git a/src/com/yj/utils/JDBCUtils.java b/src/com/yj/utils/JDBCUtils.java index e189e44..f991314 100644 --- a/src/com/yj/utils/JDBCUtils.java +++ b/src/com/yj/utils/JDBCUtils.java @@ -1,79 +1,107 @@ package com.yj.utils; +// 声明包名 import com.alibaba.druid.pool.DruidDataSource; +// 导入Druid连接池的数据源类 import com.alibaba.druid.pool.DruidDataSourceFactory; +// 导入Druid连接池的工厂类 import com.alibaba.druid.util.JdbcUtils; +// 导入Druid的工具类(注意:此处的JdbcUtils并非JDK自带的,而是Druid框架提供的,但在此代码中未直接使用) import java.io.InputStream; +// 导入输入流接口 import java.sql.Connection; +// 导入数据库连接接口 import java.sql.SQLException; +// 导入SQL异常类 import java.util.Properties; +// 导入属性类 /** * @author yj * @create 2020-08-21 10:48 */ public class JDBCUtils { + // 定义一个工具类 private static DruidDataSource dataSource; + // 定义一个静态的Druid数据源对象 private static ThreadLocal conns = new ThreadLocal(); + // 定义一个ThreadLocal对象,用于存储每个线程的数据库连接 static { + // 静态代码块,用于初始化静态变量 try { Properties properties = new Properties(); - //读取jdbc.properties属性配置文件 - InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties" ); - //从流中加载数据 + // 创建一个属性对象 + // 读取jdbc.properties属性配置文件 + InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties"); + // 从流中加载数据到属性对象中 properties.load(inputStream); - //创建数据库连接池 + // 创建数据库连接池 dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties); + // 使用Druid工厂类创建数据源对象 } catch (Exception e) { + // 捕获异常 e.printStackTrace(); + // 打印异常堆栈信息 } - } - - - - - /* - 获取数据库连接池的连接 - @retrun 如果返回null则连接失败 + /** + * 获取数据库连接池的连接 + * @return 如果返回null则连接失败 */ public static Connection getConnection() { - Connection conn = conns.get(); - if(conn == null) { - try { - conn = dataSource.getConnection();//从数据库池中获取连接 - conns.set(conn);//保存到ThreadLocal对象中,供后面的JDBC操作使用 - conn.setAutoCommit(false);//设置为手动管理事务 - } catch (SQLException throwables) { - throwables.printStackTrace(); - } - } - return conn; + // 定义一个静态方法,用于获取数据库连接 + Connection conn = conns.get(); + // 从ThreadLocal对象中获取当前线程的连接 + if(conn == null) { + // 如果当前线程没有连接 + try { + conn = dataSource.getConnection(); + // 从数据库池中获取连接 + conns.set(conn); + // 将连接保存到ThreadLocal对象中,供后面的JDBC操作使用 + conn.setAutoCommit(false); + // 设置为手动管理事务 + } catch (SQLException throwables) { + // 捕获SQL异常 + throwables.printStackTrace(); + // 打印异常堆栈信息 + } + } + return conn; + // 返回连接 } /** * 提交事务并关闭释放连接 */ public static void commitAndClose() { + // 定义一个静态方法,用于提交事务并关闭连接 Connection connection = conns.get(); + // 从ThreadLocal对象中获取连接 if(connection!=null) { + // 如果连接不为空 try { - connection.commit();//提交事务 + connection.commit();// 提交事务 } catch (SQLException throwables) { + // 捕获SQL异常 throwables.printStackTrace(); + // 打印异常堆栈信息 }finally { try { - connection.close();//关闭连接释放资源 + connection.close(); + // 关闭连接释放资源 } catch (SQLException throwables) { + // 捕获SQL异常 throwables.printStackTrace(); + // 打印异常堆栈信息 } } } - //一定要执行remove操作,否则就会出错,因为tomcat底层使用了线程池技术 + // 移除ThreadLocal对象中的连接,防止内存泄漏(特别是在使用线程池时) conns.remove(); } @@ -81,35 +109,45 @@ public class JDBCUtils { * 回滚事务并关闭释放连接 */ public static void rollbackAndClose() { + // 定义一个静态方法,用于回滚事务并关闭连接 Connection connection = conns.get(); + // 从ThreadLocal对象中获取连接 if(connection!=null) { + // 如果连接不为空 try { - connection.rollback();//回滚事务 + connection.rollback(); + // 回滚事务 } catch (SQLException throwables) { + // 捕获SQL异常 throwables.printStackTrace(); + // 打印异常堆栈信息 }finally { try { - connection.close();//关闭连接释放资源 + connection.close(); + // 关闭连接释放资源 } catch (SQLException throwables) { + // 捕获SQL异常 throwables.printStackTrace(); + // 打印异常堆栈信息 } } } - //一定要执行remove操作,否则就会出错,因为tomcat底层使用了线程池技术 + // 移除ThreadLocal对象中的连接,防止内存泄漏(特别是在使用线程池时) conns.remove(); } /* - 关闭连接,放回数据库连接池 - + (注释掉的代码)关闭连接,放回数据库连接池 + (注意:由于使用了连接池,通常不需要手动关闭连接放回池中,连接池会自动管理连接的回收和重用) public static void close(Connection conn) { if(conn != null) { try { conn.close(); + // 这里实际上并不是将连接关闭,而是将连接归还给连接池(对于连接池而言) } catch (SQLException throwables) { throwables.printStackTrace(); } } }*/ -} +} \ No newline at end of file diff --git a/src/com/yj/utils/PaymentUtil.java b/src/com/yj/utils/PaymentUtil.java index fbdb457..a3ba130 100644 --- a/src/com/yj/utils/PaymentUtil.java +++ b/src/com/yj/utils/PaymentUtil.java @@ -12,10 +12,11 @@ import java.util.Arrays; public class PaymentUtil { + // 定义字符编码 private static String encodingCharset = "UTF-8"; /** - * 生成hmac方法 + * 生成hmac方法,用于构建支付请求的参数签名 * * @param p0_Cmd 业务类型 * @param p1_MerId 商户编号 @@ -31,45 +32,34 @@ public class PaymentUtil { * @param pd_FrpId 银行编码 * @param pr_NeedResponse 应答机制 * @param keyValue 商户密钥 - * @return + * @return 生成的hmac签名 */ - public static String buildHmac(String p0_Cmd,String p1_MerId, - String p2_Order, String p3_Amt, String p4_Cur,String p5_Pid, String p6_Pcat, - String p7_Pdesc,String p8_Url, String p9_SAF,String pa_MP,String pd_FrpId, - String pr_NeedResponse,String keyValue) { + public static String buildHmac(String p0_Cmd, String p1_MerId, + String p2_Order, String p3_Amt, String p4_Cur, String p5_Pid, String p6_Pcat, + String p7_Pdesc, String p8_Url, String p9_SAF, String pa_MP, String pd_FrpId, + String pr_NeedResponse, String keyValue) { StringBuilder sValue = new StringBuilder(); - // 业务类型 + // 依次追加所有参数到StringBuilder sValue.append(p0_Cmd); - // 商户编号 sValue.append(p1_MerId); - // 商户订单号 sValue.append(p2_Order); - // 支付金额 sValue.append(p3_Amt); - // 交易币种 sValue.append(p4_Cur); - // 商品名称 sValue.append(p5_Pid); - // 商品种类 sValue.append(p6_Pcat); - // 商品描述 sValue.append(p7_Pdesc); - // 商户接收支付成功数据的地址 sValue.append(p8_Url); - // 送货地址 sValue.append(p9_SAF); - // 商户扩展信息 sValue.append(pa_MP); - // 银行编码 sValue.append(pd_FrpId); - // 应答机制 sValue.append(pr_NeedResponse); + // 调用hmacSign方法生成签名 return PaymentUtil.hmacSign(sValue.toString(), keyValue); } /** - * 返回校验hmac方法 + * 验证支付网关回调的hmac签名 * * @param hmac 支付网关发来的加密验证码 * @param p1_MerId 商户编号 @@ -84,57 +74,54 @@ public class PaymentUtil { * @param r8_MP 商户扩展信息 * @param r9_BType 交易结果返回类型 * @param keyValue 密钥 - * @return + * @return 验证结果,true表示验证通过 */ public static boolean verifyCallback(String hmac, String p1_MerId, String r0_Cmd, String r1_Code, String r2_TrxId, String r3_Amt, String r4_Cur, String r5_Pid, String r6_Order, String r7_Uid, String r8_MP, String r9_BType, String keyValue) { StringBuilder sValue = new StringBuilder(); - // 商户编号 + // 依次追加所有回调参数到StringBuilder sValue.append(p1_MerId); - // 业务类型 sValue.append(r0_Cmd); - // 支付结果 sValue.append(r1_Code); - // 易宝支付交易流水号 sValue.append(r2_TrxId); - // 支付金额 sValue.append(r3_Amt); - // 交易币种 sValue.append(r4_Cur); - // 商品名称 sValue.append(r5_Pid); - // 商户订单号 sValue.append(r6_Order); - // 易宝支付会员ID sValue.append(r7_Uid); - // 商户扩展信息 sValue.append(r8_MP); - // 交易结果返回类型 sValue.append(r9_BType); + // 调用hmacSign方法生成签名并与支付网关的签名比较 String sNewString = PaymentUtil.hmacSign(sValue.toString(), keyValue); return sNewString.equals(hmac); } /** - * @param aValue - * @param aKey - * @return + * 使用HMAC-MD5算法生成签名 + * + * @param aValue 待签名的字符串 + * @param aKey 密钥 + * @return 生成的签名 */ public static String hmacSign(String aValue, String aKey) { + // 初始化内部填充字节数组 byte k_ipad[] = new byte[64]; byte k_opad[] = new byte[64]; byte keyb[]; byte value[]; try { + // 使用指定的字符编码获取字节数组 keyb = aKey.getBytes(encodingCharset); value = aValue.getBytes(encodingCharset); } catch (UnsupportedEncodingException e) { + // 如果不支持指定的字符编码,则使用默认字符编码 keyb = aKey.getBytes(); value = aValue.getBytes(); } + // 填充k_ipad和k_opad数组 Arrays.fill(k_ipad, keyb.length, 64, (byte) 54); Arrays.fill(k_opad, keyb.length, 64, (byte) 92); for (int i = 0; i < keyb.length; i++) { @@ -142,23 +129,32 @@ public class PaymentUtil { k_opad[i] = (byte) (keyb[i] ^ 0x5c); } + // 获取MD5算法的MessageDigest实例 MessageDigest md = null; try { md = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { - + // 如果MD5算法不可用,则返回null return null; } + // 对k_ipad和待签名数据进行更新 md.update(k_ipad); md.update(value); - byte dg[] = md.digest(); - md.reset(); + byte dg[] = md.digest(); // 进行第一次摘要 + md.reset(); // 重置MessageDigest实例 + // 对k_opad和第一次摘要结果进行更新 md.update(k_opad); md.update(dg, 0, 16); - dg = md.digest(); - return toHex(dg); + dg = md.digest(); // 进行第二次摘要,得到最终的签名 + return toHex(dg); // 将字节数组转换为十六进制字符串 } + /** + * 将字节数组转换为十六进制字符串 + * + * @param input 字节数组 + * @return 十六进制字符串 + */ public static String toHex(byte input[]) { if (input == null) return null; @@ -169,15 +165,15 @@ public class PaymentUtil { output.append("0"); output.append(Integer.toString(current, 16)); } - return output.toString(); } /** + * 根据参数数组和密钥生成HMAC签名 * - * @param args - * @param key - * @return + * @param args 参数数组 + * @param key 密钥 + * @return 生成的HMAC签名 */ public static String getHmac(String[] args, String key) { if (args == null || args.length == 0) { @@ -191,8 +187,10 @@ public class PaymentUtil { } /** - * @param aValue - * @return + * 使用SHA算法对字符串进行摘要 + * + * @param aValue 待摘要的字符串 + * @return 摘要结果的十六进制字符串 */ public static String digest(String aValue) { aValue = aValue.trim(); diff --git a/src/com/yj/web/CartServlet.java b/src/com/yj/web/CartServlet.java index cfa53cd..b6beb41 100644 --- a/src/com/yj/web/CartServlet.java +++ b/src/com/yj/web/CartServlet.java @@ -1,12 +1,20 @@ +// 导入所需的包和类 package com.yj.web; import com.google.gson.Gson; +// 用于将Java对象转换为JSON字符串 import com.yj.bean.Book; +// 书籍实体类 import com.yj.bean.Cart; +// 购物车实体类 import com.yj.bean.CartItem; +// 购物车商品项实体类 import com.yj.service.BookService; +// 书籍服务接口 import com.yj.service.impl.BookServiceImpl; +// 书籍服务接口的实现类 import com.yj.utils.WebUtils; +// 工具类,用于处理Web请求中的常见操作 import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -15,100 +23,93 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; -/** - * @author yj - * @create 2020-08-27 9:47 - */ +// 定义一个Servlet类,用于处理与购物车相关的请求 public class CartServlet extends BaseServlet { + // 创建一个书籍服务实例,用于访问书籍数据 private BookService bookService = new BookServiceImpl(); + // 处理通过AJAX方式添加商品到购物车的请求 protected void ajaxAddItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - int id = WebUtils.parseInt(req.getParameter("id"),0); + // 从请求中获取书籍ID,并转换为整数类型,默认为0 + int id = WebUtils.parseInt(req.getParameter("id"), 0); + // 根据书籍ID查询书籍信息 Book book = bookService.queryBookById(id); - CartItem cartItem = new CartItem(book.getId(),book.getName(),1,book.getPrice(),book.getPrice()); + // 创建一个购物车商品项实例,数量为1,单价和总价均为书籍价格 + CartItem cartItem = new CartItem(book.getId(), book.getName(), 1, book.getPrice(), book.getPrice()); + // 从会话中获取购物车对象,如果不存在则创建一个新的购物车对象 Cart cart = (Cart) req.getSession().getAttribute("cart"); - if(cart==null) { + if (cart == null) { cart = new Cart(); - req.getSession().setAttribute("cart",cart); + req.getSession().setAttribute("cart", cart); } + // 将商品项添加到购物车中 cart.addItem(cartItem); - req.getSession().setAttribute("lastName",cartItem.getName()); + // 在会话中保存最后添加的商品名称(可能用于显示或提示) + req.getSession().setAttribute("lastName", cartItem.getName()); - //返回购物车总数量和最后一个商品的名称 - Map resultMap = new HashMap(); - resultMap.put("totalCount",cart.getTotalCount()); - resultMap.put("lastName",cartItem.getName()); + // 准备返回给客户端的数据,包括购物车总数量和最后一个商品的名称 + Map resultMap = new HashMap(); + resultMap.put("totalCount", cart.getTotalCount()); + resultMap.put("lastName", cartItem.getName()); + // 使用Gson将Map对象转换为JSON字符串 Gson gson = new Gson(); String resultMapJsonString = gson.toJson(resultMap); + // 将JSON字符串写入响应中 resp.getWriter().write(resultMapJsonString); - } - /** - * 加入购物车 - * @param req - * @param resp - * @throws ServletException - * @throws IOException - */ + + // 处理添加商品到购物车的请求(非AJAX方式) protected void addItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - int id = WebUtils.parseInt(req.getParameter("id"),0); - Book book = bookService.queryBookById(id); - CartItem cartItem = new CartItem(book.getId(),book.getName(),1,book.getPrice(),book.getPrice()); + // 以下代码与ajaxAddItem方法中的代码大致相同,只是最后返回的是页面重定向 + int id = WebUtils.parseInt(req.getParameter("id"), 0); + Book book = bookService.queryBookById(id); + CartItem cartItem = new CartItem(book.getId(), book.getName(), 1, book.getPrice(), book.getPrice()); Cart cart = (Cart) req.getSession().getAttribute("cart"); - if(cart==null) { + if (cart == null) { cart = new Cart(); - req.getSession().setAttribute("cart",cart); + req.getSession().setAttribute("cart", cart); } cart.addItem(cartItem); - req.getSession().setAttribute("lastName",cartItem.getName()); + req.getSession().setAttribute("lastName", cartItem.getName()); + // 重定向到请求来源页面(通常是通过Referer头部获取的) resp.sendRedirect(req.getHeader("Referer")); } - /** - * 删除商品项 - * @param req - * @param resp - * @throws ServletException - * @throws IOException - */ + // 处理删除购物车中商品项的请求 protected void deleteItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - int id = WebUtils.parseInt(req.getParameter("id"),0); + // 从请求中获取要删除的商品项ID + int id = WebUtils.parseInt(req.getParameter("id"), 0); + // 从会话中获取购物车对象 Cart cart = (Cart) req.getSession().getAttribute("cart"); - if(cart!=null) { + // 如果购物车对象存在,则删除指定ID的商品项 + if (cart != null) { cart.deleteItem(id); - resp.sendRedirect(req.getHeader("Referer")); } + // 重定向到请求来源页面 + resp.sendRedirect(req.getHeader("Referer")); } - /** - * 清空商品项 - * @param req - * @param resp - * @throws ServletException - * @throws IOException - */ + // 处理清空购物车中所有商品项的请求 protected void clearItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - req.getSession().removeAttribute("cart"); + // 从会话中移除购物车对象,从而清空购物车 + req.getSession().removeAttribute("cart"); + // 重定向到请求来源页面 resp.sendRedirect(req.getHeader("Referer")); } - /** - * 修改商品数量 - * @param req - * @param resp - * @throws ServletException - * @throws IOException - */ + // 处理修改购物车中商品项数量的请求 protected void updateCount(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - int id = WebUtils.parseInt(req.getParameter("id"),0); - int count = WebUtils.parseInt(req.getParameter("count"),1); + // 从请求中获取要修改的商品项ID和新的数量 + int id = WebUtils.parseInt(req.getParameter("id"), 0); + int count = WebUtils.parseInt(req.getParameter("count"), 1); + // 从会话中获取购物车对象 Cart cart = (Cart) req.getSession().getAttribute("cart"); - if(cart!=null) { - cart.updateCount(id,count); - resp.sendRedirect(req.getHeader("Referer")); + // 如果购物车对象存在,则修改指定ID的商品项数量 + if (cart != null) { + cart.updateCount(id, count); } - + // 重定向到请求来源页面 + resp.sendRedirect(req.getHeader("Referer")); } - -} +} \ No newline at end of file diff --git a/web/pages/cart/cart.jsp b/web/pages/cart/cart.jsp index 84c5289..60c7d91 100644 --- a/web/pages/cart/cart.jsp +++ b/web/pages/cart/cart.jsp @@ -1,28 +1,45 @@ <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + <%@ page contentType="text/html;charset=UTF-8" language="java" %> + + - -购物车 + + + 购物车 + <%@include file="/pages/common/header.jsp"%> + - - - -
- - + + + +
+ + +
+ + + + + + + + + + + - - - - - + + + - + + + + - + + + + + + + + + - + + - - - - - - - - - - - - -
商品名称数量单价金额操作
商品名称数量单价金额操作亲,当前购物车为空,快去和小伙伴浏览书籍吧!
亲,当前购物车为空,快去和小伙伴浏览书籍吧! ${entry.value.name} + + + ${entry.value.price}${entry.value.totalPrice}删除
${entry.value.name} - - ${entry.value.price}${entry.value.totalPrice}删除
- + + +
+ 购物车中共有${sessionScope.cart.totalCount}本书籍 + 总金额${sessionScope.cart.totalPrice} + 清空购物车 + 去结账 +
-
+
-
+ - <%@include file="/pages/common/footer.jsp"%> +<%@include file="/pages/common/footer.jsp"%> + - + \ No newline at end of file diff --git a/web/pages/cart/checkout.jsp b/web/pages/cart/checkout.jsp index 8d4efd6..313620c 100644 --- a/web/pages/cart/checkout.jsp +++ b/web/pages/cart/checkout.jsp @@ -1,32 +1,52 @@ <%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + - -结算页面 + + + + 结算页面 + <%@include file="/pages/common/header.jsp"%> - + - - - -
- -

你的订单已结算,订单号为${sessionScope.orderId},店主很快就会发货啦!

- - -
+ - <%@include file="/pages/common/footer.jsp"%> + + +
+ + +

你的订单已结算,订单号为${sessionScope.orderId},店主很快就会发货啦!

+ + + +
+ +<%@include file="/pages/common/footer.jsp"%> + - \ No newline at end of file + + \ No newline at end of file diff --git a/web/pages/cart/pay.jsp b/web/pages/cart/pay.jsp index 6db9fe5..bc4b993 100644 --- a/web/pages/cart/pay.jsp +++ b/web/pages/cart/pay.jsp @@ -1,78 +1,98 @@ <%-- + 这是一个JSP注释,不会被发送到客户端。它包含了文件的创建信息。 Created by IntelliJ IDEA. User: jhu Date: 2020/10/5 Time: 14:50 To change this template use File | Settings | File Templates. --%> + <%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + + 结算页面 + + <%@include file="/pages/common/header.jsp"%> + + + + + +
+
+ + + + + 支付金额:元 - + + + + - + - + - + - - + + - + + - + +
- 支付金额:

-

请您选择在线支付银行
招商银行 - 招商银行 工商银行 农业银行建设银行 - 建设银行
中国民生银行总行光大银行 - 光大银行 交通银行 深圳发展银行
北京银行兴业银行 - 上海浦东发展银行 - 兴业银行上海浦东发展银行 中信银行

-

-
-
<%@include file="/pages/common/footer.jsp"%> + - - + + \ No newline at end of file diff --git a/web/static/script/jquery-1.7.2.js b/web/static/script/jquery-1.7.2.js index 391f195..53ce4bb 100644 --- a/web/static/script/jquery-1.7.2.js +++ b/web/static/script/jquery-1.7.2.js @@ -4306,124 +4306,203 @@ var getText = Sizzle.getText = function( elem ) { return ret; }; -var Expr = Sizzle.selectors = { - order: [ "ID", "NAME", "TAG" ], - - match: { - ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, - CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, - NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, - ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, - TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, - CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, - POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, - PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ - }, - - leftMatch: {}, - - attrMap: { - "class": "className", - "for": "htmlFor" - }, - - attrHandle: { - href: function( elem ) { - return elem.getAttribute( "href" ); + // 定义Expr对象,它包含选择器引擎的一些核心属性和方法,Sizzle.selectors将其引用为Expr + var Expr = Sizzle.selectors = { + // 定义选择器匹配的优先级顺序 + order: [ "ID", "NAME", "TAG" ], + + // 定义一个对象,包含不同类型的选择器匹配正则表达式 + match: { + // ID选择器,匹配以#开头的字符串,后面跟随一个或多个字母、数字、连字符、Unicode字符或转义字符 + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + // 类选择器,匹配以.开头的字符串,后面跟随一个或多个字母、数字、连字符、Unicode字符或转义字符 + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + // 名称选择器,匹配[name='value']的形式,其中value可以是字母、数字、连字符、Unicode字符或转义字符 + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + // 属性选择器,匹配[attr=value]的形式,支持多种属性值和复杂的表达式 + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + // 标签选择器,匹配一个或多个字母、数字、星号、连字符、Unicode字符或转义字符 + TAG: /^((?:[\w\u00c0-\uFFFF*\-]|\\.)+)/, + // 子选择器,匹配:first-child, :last-child, :nth-child(n), :only-child等 + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + // 位置选择器,匹配:nth, :eq, :gt, :lt, :first, :last, :even, :odd等 + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + // 伪类选择器,匹配形如:hover, :focus等的伪类,支持带参数的伪类如:not(.class) + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ }, - type: function( elem ) { - return elem.getAttribute( "type" ); - } - }, - - relative: { - "+": function(checkSet, part){ - var isPartStr = typeof part === "string", - isTag = isPartStr && !rNonWord.test( part ), - isPartStrNotTag = isPartStr && !isTag; - - if ( isTag ) { - part = part.toLowerCase(); - } - for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { - if ( (elem = checkSet[i]) ) { - while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + // leftMatch对象,可能用于存储一些预处理或解析过程中使用的匹配信息 + leftMatch: {}, - checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? - elem || false : - elem === part; - } - } + // attrMap对象,定义了一些属性名的映射,用于处理某些HTML属性在DOM中的不同表示 + attrMap: { + "class": "className", + // class属性在DOM中对应className + "for": "htmlFor" + // for属性在DOM中对应htmlFor + }, - if ( isPartStrNotTag ) { - Sizzle.filter( part, checkSet, true ); + // attrHandle对象,定义了一些特定属性的处理函数 + attrHandle: { + // href属性的处理函数,返回元素的href属性值 + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + // type属性的处理函数,返回元素的type属性值 + type: function( elem ) { + return elem.getAttribute( "type" ); } }, - ">": function( checkSet, part ) { - var elem, - isPartStr = typeof part === "string", - i = 0, - l = checkSet.length; - - if ( isPartStr && !rNonWord.test( part ) ) { - part = part.toLowerCase(); - - for ( ; i < l; i++ ) { - elem = checkSet[i]; - - if ( elem ) { - var parent = elem.parentNode; - checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + // 定义一个对象,包含处理相对选择器的函数 + relative: { + // "+" 选择器的处理函数,用于查找当前元素之前的相邻兄弟元素 + "+": function(checkSet, part){ + // 判断part是否为字符串类型 + var isPartStr = typeof part === "string", + // 判断part是否为标签名(即不包含非单词字符) + isTag = isPartStr && !rNonWord.test( part ), + // 判断part是否为字符串但不是标签名(可能是一个类名、ID等) + isPartStrNotTag = isPartStr && !isTag; + + // 如果part是标签名,则将其转换为小写 + if ( isTag ) { + part = part.toLowerCase(); } - } - } else { - for ( ; i < l; i++ ) { - elem = checkSet[i]; + // 遍历checkSet中的每个元素 + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + // 获取当前遍历到的元素 + if ( (elem = checkSet[i]) ) { + // 向上遍历前一个兄弟元素,直到找到一个元素节点(nodeType为1)或没有兄弟元素为止 + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + // 根据条件更新checkSet[i]的值 + // 如果part不是标签名,或者elem存在且其标签名与part(小写)相同,则设置为elem或false(如果elem不存在) + // 如果part是具体的某个元素(通过===比较),则直接比较elem和part是否相同 + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + // 如果条件满足,设置为elem(如果存在)或false(如果不存在) + elem === part; + // 如果part是具体的元素,直接比较是否相等 + } + } - if ( elem ) { - checkSet[i] = isPartStr ? - elem.parentNode : - elem.parentNode === part; + // 如果part是字符串但不是标签名(可能是一个类名、ID等),则调用Sizzle的filter函数来处理 + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); } - } +// “>” 选择器的处理函数,用于查找当前元素的直接子元素 + }, + ">": function( checkSet, part ) { + var elem, + // 当前遍历到的元素 + isPartStr = typeof part === "string", + // 判断part是否为字符串类型 + i = 0, + // 循环计数器 + l = checkSet.length; + // checkSet的长度 + + // 如果part是字符串且不包含非单词字符(可能是标签名),则进行以下处理 + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + // 将part转换为小写,以确保比较时不区分大小写 + + // 遍历checkSet中的每个元素 + for ( ; i < l; i++ ) { + elem = checkSet[i]; + // 获取当前遍历到的元素 + + if ( elem ) { + var parent = elem.parentNode; + // 获取当前元素的父元素 + // 如果当前元素的父元素的标签名与part(小写)相同,则将该父元素设置为checkSet[i]的值,否则设置为false + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } +// 注意:这里的代码片段没有包含rNonWord正则表达式的定义,也没有包含函数的结束括号和对象字面量的结束花括号。 + } // 如果part不是简单的标签名(即包含非单词字符或未定义rNonWord导致的其他情况) + else { + // 遍历checkSet中的每个元素 + for (; i < l; i++) { + elem = checkSet[i]; + // 获取当前遍历到的元素 + + if (elem) { + // 根据part是否为字符串类型,更新checkSet[i]的值 + // 如果是字符串,则保留elem的父元素;如果不是(可能是具体的DOM元素),则比较elem的父元素是否等于part + checkSet[i] = isPartStr ? + elem.parentNode : + // 如果part是字符串,则设置为elem的父元素 + elem.parentNode === part; + // 如果part不是字符串(可能是DOM元素),则比较elem的父元素是否等于part + } + } - if ( isPartStr ) { - Sizzle.filter( part, checkSet, true ); - } - } - }, + // 如果part是字符串类型,则对checkSet进行进一步的筛选 + // 这可能是为了处理类名、ID或其他属性选择器的情况 + if (isPartStr) { + Sizzle.filter(part, checkSet, true); + // 调用Sizzle的filter函数进行筛选,true可能是表示某种特定行为的标志 + } + } + }, +// 注意:这里的代码片段是“>”选择器的处理函数的一部分,并且没有包含函数的结束括号和对象字面量的结束花括号。 +// 此外,还假设了Sizzle.filter函数的存在和行为,以及isPartStr、checkSet、part、i和l等变量的上下文。 +// 定义一个对象字面量,其中包含不同选择器的处理函数 + // 空字符串选择器的处理函数 "": function(checkSet, part, isXML){ - var nodeCheck, - doneName = done++, - checkFn = dirCheck; - - if ( typeof part === "string" && !rNonWord.test( part ) ) { - part = part.toLowerCase(); - nodeCheck = part; - checkFn = dirNodeCheck; - } - - checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); - }, - + var nodeCheck, + // 用于存储节点检查的变量(可能是标签名) + doneName = done++, + // 一个唯一标识符,可能用于跟踪检查过程的状态,`done`可能是一个外部变量,在此函数中自增 + checkFn = dirCheck; + // 初始检查函数,可能用于执行一般的DOM树遍历和检查 + + // 如果part是字符串且不包含非单词字符,则进行以下处理 + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + // 将part转换为小写 + nodeCheck = part; + // 将part设置为nodeCheck,用于后续的节点检查 + checkFn = dirNodeCheck; + // 将检查函数更改为dirNodeCheck,可能用于基于节点名的检查 + } + + // 调用检查函数,传入相关参数以执行检查逻辑 + // "parentNode"表示要检查的DOM关系(在这个上下文中,可能是用于遍历父元素) + // part、doneName、checkSet、nodeCheck和isXML是其他必要的参数 + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + // 波浪线选择器(~)的处理函数,用于选择同一父元素下的兄弟元素 "~": function( checkSet, part, isXML ) { - var nodeCheck, - doneName = done++, - checkFn = dirCheck; - - if ( typeof part === "string" && !rNonWord.test( part ) ) { - part = part.toLowerCase(); - nodeCheck = part; - checkFn = dirNodeCheck; - } - - checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); - } + var nodeCheck, // 同上,用于存储节点检查的变量 + doneName = done++, + // 同上,生成一个唯一标识符 + checkFn = dirCheck; + // 同上,初始检查函数 + + // 如果part是字符串且不包含非单词字符,则进行与空字符串选择器相同的处理 + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + // 转换为小写 + nodeCheck = part; + // 设置为nodeCheck + checkFn = dirNodeCheck; + // 更改检查函数为dirNodeCheck + } + + // 调用检查函数,但这次传入"previousSibling"作为要检查的DOM关系 + // 这表明我们是在查找同一父元素下的前一个兄弟元素(但实际上可能是通过逻辑来查找所有匹配的兄弟元素) + // 注意:这里的实现细节可能有所不同,因为波浪线选择器通常需要检查所有兄弟元素,而不仅仅是前一个 + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } }, +// 注意:这里的代码片段没有包含`done`变量的定义、`dirCheck`和`dirNodeCheck`函数的定义,以及对象字面量的结束花括号。 +// 这些都是在外部定义的,可能是选择器引擎的一部分。 find: { ID: function( match, context, isXML ) { From 33b73603ffbbe4e9dd62646e8f5527ec5debd15e Mon Sep 17 00:00:00 2001 From: LFE <756741044@qq.com> Date: Sat, 14 Dec 2024 23:14:14 +0800 Subject: [PATCH 3/3] lfe --- web/static/script/jquery-1.7.2.js | 1466 +++++++++++++++++------------ 1 file changed, 848 insertions(+), 618 deletions(-) diff --git a/web/static/script/jquery-1.7.2.js b/web/static/script/jquery-1.7.2.js index 53ce4bb..36f1f67 100644 --- a/web/static/script/jquery-1.7.2.js +++ b/web/static/script/jquery-1.7.2.js @@ -4504,755 +4504,985 @@ var getText = Sizzle.getText = function( elem ) { // 注意:这里的代码片段没有包含`done`变量的定义、`dirCheck`和`dirNodeCheck`函数的定义,以及对象字面量的结束花括号。 // 这些都是在外部定义的,可能是选择器引擎的一部分。 - find: { - ID: function( match, context, isXML ) { - if ( typeof context.getElementById !== "undefined" && !isXML ) { - var m = context.getElementById(match[1]); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [m] : []; - } - }, - - NAME: function( match, context ) { - if ( typeof context.getElementsByName !== "undefined" ) { - var ret = [], - results = context.getElementsByName( match[1] ); - - for ( var i = 0, l = results.length; i < l; i++ ) { - if ( results[i].getAttribute("name") === match[1] ) { - ret.push( results[i] ); - } + find: { + // ID选择器函数,用于根据元素的ID查找元素 + ID: function( match, context, isXML ) { + // 如果上下文对象(context)有getElementById方法,并且不是在XML文档中 + if ( typeof context.getElementById !== "undefined" && !isXML ) { + // 使用getElementById方法获取匹配的元素 + var m = context.getElementById(match[1]); + // 检查获取到的元素是否有父节点,以处理Blackberry 4.6返回的不在文档中的节点的问题 + // #6963是一个可能是相关的bug编号 + return m && m.parentNode ? [m] : []; } + }, - return ret.length === 0 ? null : ret; - } - }, - - TAG: function( match, context ) { - if ( typeof context.getElementsByTagName !== "undefined" ) { - return context.getElementsByTagName( match[1] ); - } - } - }, - preFilter: { - CLASS: function( match, curLoop, inplace, result, not, isXML ) { - match = " " + match[1].replace( rBackslash, "" ) + " "; - - if ( isXML ) { - return match; - } - - for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { - if ( elem ) { - if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { - if ( !inplace ) { - result.push( elem ); + // NAME选择器函数,用于根据元素的name属性查找元素 + NAME: function( match, context ) { + // 如果上下文对象(context)有getElementsByName方法 + if ( typeof context.getElementsByName !== "undefined" ) { + // 初始化一个空数组,用于存储匹配的结果 + var ret = [], + // 使用getElementsByName方法获取所有name属性匹配的元素 + results = context.getElementsByName(match[1]); + + // 遍历所有获取到的元素 + for (var i = 0, l = results.length; i < l; i++) { + // 如果元素的name属性确实匹配查找的值 + if (results[i].getAttribute("name") === match[1]) { + // 将该元素添加到结果数组中 + ret.push(results[i]); } - - } else if ( inplace ) { - curLoop[i] = false; } - } - } - return false; - }, - - ID: function( match ) { - return match[1].replace( rBackslash, "" ); - }, - - TAG: function( match, curLoop ) { - return match[1].replace( rBackslash, "" ).toLowerCase(); - }, - - CHILD: function( match ) { - if ( match[1] === "nth" ) { - if ( !match[2] ) { - Sizzle.error( match[0] ); + // 如果没有找到任何匹配的元素,返回null;否则返回结果数组 + return ret.length === 0 ? null : ret; + } } + }, - match[2] = match[2].replace(/^\+|\s*/g, ''); - - // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' - var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( - match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || - !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); - - // calculate the numbers (first)n+(last) including if they are negative - match[2] = (test[1] + (test[2] || 1)) - 0; - match[3] = test[3] - 0; - } - else if ( match[2] ) { - Sizzle.error( match[0] ); - } - - // TODO: Move to normal caching system - match[0] = done++; - - return match; - }, - - ATTR: function( match, curLoop, inplace, result, not, isXML ) { - var name = match[1] = match[1].replace( rBackslash, "" ); - - if ( !isXML && Expr.attrMap[name] ) { - match[1] = Expr.attrMap[name]; - } - - // Handle if an un-quoted value was used - match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); - - if ( match[2] === "~=" ) { - match[4] = " " + match[4] + " "; - } - - return match; - }, - - PSEUDO: function( match, curLoop, inplace, result, not ) { - if ( match[1] === "not" ) { - // If we're dealing with a complex expression, or a simple one - if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { - match[3] = Sizzle(match[3], null, null, curLoop); - - } else { - var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + TAG: function( match, context ) { + // 如果上下文对象(context)有getElementsByTagName方法 + if ( typeof context.getElementsByTagName !== "undefined" ) { + // 使用getElementsByTagName方法查找所有匹配的标签名元素,并返回结果数组 + return context.getElementsByTagName( match[1] ); + } + }, +// 预处理过滤器对象,用于在正式查找前对选择器进行一些处理 + preFilter: { + // CLASS过滤器函数,用于处理类名选择器 + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + // 对match[1](类名字符串)进行处理,去除可能的反斜杠(这里rBackslash应该是一个正则表达式) + // 并在类名字符串的前后加上空格,这通常是为了处理类名选择器时的边界情况 + match = " " + match[1].replace( rBackslash, "" ) + " "; + + // 如果是在XML文档中,则直接返回处理后的类名字符串 + // 这里的处理可能是为了后续在XML环境中进行特定的匹配 + if ( isXML ) { + return match; + } + // 注意:此代码段在此处被截断,没有显示后续的处理逻辑 + // 通常情况下,这里会有更多的代码来处理类名选择器,尤其是在非XML环境中 + // 遍历curLoop数组中的每个元素,直到elem为null + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + // 如果elem不是假值(即elem是一个有效的DOM元素) + if ( elem ) { + // 检查elem的className是否包含match指定的类名 + // not是一个布尔值,用于指示是否进行反向匹配(即排除具有该类名的元素) + // ^ 是位异或运算符,但在这里它可能是一个逻辑错误,因为通常我们会用!或==/!=来比较布尔值 + // 正确的逻辑可能是使用!或==来根据not的值决定是包含还是排除类名 + // elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0 + // 这段代码首先确保elem有className,然后将className前后加上空格,并替换掉所有的制表符、换行符和回车符,最后检查处理后的字符串中是否包含match指定的类名 + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + // 如果不在inplace模式下,并且元素匹配(或根据not的值应该被包含),则将元素添加到结果集中 + if ( !inplace ) { + result.push( elem ); + } - if ( !inplace ) { - result.push.apply( result, ret ); + } else if ( inplace ) { + // 如果在inplace模式下,并且元素不匹配(或根据not的值应该被排除),则将当前元素在curLoop中的位置设置为false + // 这意味着在后续的迭代中,这个元素将被跳过 + curLoop[i] = false; + } + } } +// 函数返回false,这可能表示在当前的处理逻辑下,没有特殊的返回值需要传递给调用者 +// 或者这可能是一个约定俗成的返回值,用于指示某种状态或条件 return false; - } - - } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { - return true; - } - - return match; - }, - - POS: function( match ) { - match.unshift( true ); - - return match; - } - }, + }, - filters: { - enabled: function( elem ) { - return elem.disabled === false && elem.type !== "hidden"; - }, + // ID选择器处理函数 + ID: function( match ) { + // 从match数组中提取ID值,并去除可能的反斜杠字符,然后返回处理后的ID字符串 + return match[1].replace( rBackslash, "" ); + }, - disabled: function( elem ) { - return elem.disabled === true; - }, +// TAG选择器处理函数 + TAG: function( match, curLoop ) { + // 从match数组中提取标签名,去除可能的反斜杠字符,并将其转换为小写,然后返回处理后的标签名字符串 + // 注意:尽管这个函数接收了curLoop参数,但在当前代码段中并没有使用它 + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, - checked: function( elem ) { - return elem.checked === true; - }, +// CHILD选择器处理函数,用于处理如:nth-child之类的伪类选择器 + CHILD: function( match ) { + // 如果匹配的是nth类型的子选择器 + if ( match[1] === "nth" ) { + // 如果没有提供nth的具体参数(如2n+1),则抛出错误 + if ( !match[2] ) { + Sizzle.error( match[0] ); + } - selected: function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } + // 去除nth参数前的加号或空格 + match[2] = match[2].replace(/^\+|\s*/g, ''); + + // 使用正则表达式解析nth参数,支持'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'等形式的表达式 + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || + // 如果是'even',则转换为'2n' + match[2] === "odd" && "2n+1" || + // 如果是'odd',则转换为'2n+1' + !/\D/.test( match[2] ) && "0n+" + match[2] || + // 如果是一个纯数字,则前面加上'0n+' + match[2] + // 如果都不匹配,则直接使用原始match[2] + ); + + // 计算nth参数中的系数和偏移量,包括处理负数的情况 + // test[1]是符号(+或-),test[2]是系数(可能是空字符串,表示默认为1),test[3]是偏移量 + match[2] = (test[1] + (test[2] || 1)) - 0; // 系数,确保是数字类型 + match[3] = test[3] - 0; + // 偏移量,确保是数字类型 + } + // 如果不是nth类型的子选择器,但提供了第二个参数(通常不应该),则抛出错误 + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + // 注意:函数没有返回值,因为它可能是通过修改match数组来影响外部状态的 - return elem.selected === true; - }, - parent: function( elem ) { - return !!elem.firstChild; - }, + // TODO: 这是一个待办事项,表示需要将这部分代码移动到正常的缓存系统中去 +// 这可能是指优化性能,通过缓存某些计算结果来避免重复计算 + match[0] = done++; + // 将match数组的第一个元素设置为一个递增的计数器值,可能用于标识处理进度或唯一性 - empty: function( elem ) { - return !elem.firstChild; - }, + return match; + // 返回处理后的match数组 + }, - has: function( elem, i, match ) { - return !!Sizzle( match[3], elem ).length; - }, +// ATTR选择器处理函数,用于处理属性选择器,如[attr=value] + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + // 从match数组中提取属性名,并去除可能的反斜杠字符 + var name = match[1] = match[1].replace( rBackslash, "" ); - header: function( elem ) { - return (/h\d/i).test( elem.nodeName ); - }, + // 如果不是在XML上下文中,并且Expr.attrMap中存在该属性名的映射 + // 则将属性名替换为映射后的名称(这通常用于处理HTML中的特殊属性) + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } - text: function( elem ) { - var attr = elem.getAttribute( "type" ), type = elem.type; - // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) - // use getAttribute instead to test this case - return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); - }, + // 处理未使用引号括起来的值(如果有的话) + // 这通常是为了确保属性值中的特殊字符被正确处理 + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); - radio: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; - }, - - checkbox: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; - }, + // 如果使用的是~=运算符(表示“包含一个单词”的匹配) + // 则在属性值的前后各添加一个空格,以确保单词边界的正确匹配 + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } - file: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; - }, + // 返回处理后的match数组 + return match; + }, + // PSEUDO函数,用于处理伪类选择器 + PSEUDO: function( match, curLoop, inplace, result, not ) { + // 检查是否处理的是:not伪类 + if ( match[1] === "not" ) { + // 如果:not内部包含复杂的表达式或简单的单词表达式 + // 使用chunker正则表达式来分割和解析表达式,或者检查表达式是否以单词字符开头 + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + // 对:not内部的表达式进行完整的Sizzle查询 + // 注意:这里假设chunker是一个能够解析复杂选择器的正则表达式 + // null参数可能表示在当前的文档或上下文中进行查询 + match[3] = Sizzle(match[3], null, null, curLoop); - password: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; - }, + } else { + // 如果:not内部是一个简单的选择器,则使用Sizzle.filter进行过滤 + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); - submit: function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && "submit" === elem.type; - }, + // 如果不是就地修改结果集 + if ( !inplace ) { + // 将过滤后的结果添加到最终的结果集中 + result.push.apply( result, ret ); + } - image: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; - }, + // 返回false,表示这个分支已经处理了结果,不需要进一步处理 + return false; + } - reset: function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && "reset" === elem.type; - }, + // 检查是否处理的是位置伪类(如:first-child)或其他Expr.match.POS定义的伪类 + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + // 返回true,表示这个选择器需要特殊处理(可能是由其他部分的代码处理) + return true; + } - button: function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && "button" === elem.type || name === "button"; - }, + // 如果没有特别处理,则返回原始的match数组 + return match; + }, + // POS函数,可能用于处理位置伪类选择器(如:first-child, :last-child等) +// 但这里的实现看起来有些简单,只是向match数组中添加了一个true值 + POS: function( match ) { + match.unshift(true); + // 在match数组的开头添加一个true值,可能表示这个选择器需要特殊处理 + + return match; + // 返回修改后的match数组 + } + }, - input: function( elem ) { - return (/input|select|textarea|button/i).test( elem.nodeName ); - }, +// filters对象,包含一系列用于过滤DOM元素的函数 + filters: { + // enabled函数,用于检查元素是否启用(即不是disabled状态且type不是hidden) + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + // 如果元素没有被禁用且type属性不是"hidden",则返回true + }, - focus: function( elem ) { - return elem === elem.ownerDocument.activeElement; - } - }, - setFilters: { - first: function( elem, i ) { - return i === 0; - }, + // disabled函数,用于检查元素是否被禁用 + disabled: function( elem ) { + return elem.disabled === true; + // 如果元素的disabled属性为true,则返回true + }, - last: function( elem, i, match, array ) { - return i === array.length - 1; - }, + // checked函数,用于检查元素是否被选中(通常用于checkbox和radio按钮) + checked: function( elem ) { + return elem.checked === true; + // 如果元素的checked属性为true,则返回true + }, - even: function( elem, i ) { - return i % 2 === 0; - }, + // selected函数,用于检查元素是否被选中(通常用于